github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/constraint/constraint.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/cockroach/pkg/sql/types"
    19  	"github.com/cockroachdb/errors"
    20  )
    21  
    22  // Constraint specifies the possible set of values that one or more columns
    23  // will have in the result set. If this is a single column constraint, then
    24  // that column's value will always be part of one of the spans in this
    25  // constraint. If this is a multi-column constraint, then the combination of
    26  // column values will always be part of one of the spans.
    27  //
    28  // Constrained columns are specified as an ordered list, and span key values
    29  // correspond to those columns by position. Constraints are inferred from
    30  // scalar filter conditions, since these restrict the set of possible results.
    31  // Constraints over different combinations of columns can be combined together
    32  // in a constraint set, which is a conjunction of constraints. See the
    33  // Set struct comment for more details.
    34  //
    35  // A few examples:
    36  //  - a constraint on @1 > 1: a single span             /@1: (/1 - ]
    37  //  - a constraint on @1 = 1 AND @2 >= 1: a single span /@1/@2: [/1/1 - /1]
    38  //  - a constraint on @1 < 5 OR @1 > 10: multiple spans /@1: [ - /5) (10 - ]
    39  type Constraint struct {
    40  	Columns Columns
    41  
    42  	// Spans contains the spans in this constraint. The spans are always ordered
    43  	// and non-overlapping.
    44  	Spans Spans
    45  }
    46  
    47  // Init initializes the constraint to the columns in the key context and with
    48  // the given spans.
    49  func (c *Constraint) Init(keyCtx *KeyContext, spans *Spans) {
    50  	for i := 1; i < spans.Count(); i++ {
    51  		if !spans.Get(i).StartsStrictlyAfter(keyCtx, spans.Get(i-1)) {
    52  			panic(errors.AssertionFailedf("spans must be ordered and non-overlapping"))
    53  		}
    54  	}
    55  	c.Columns = keyCtx.Columns
    56  	c.Spans = *spans
    57  	c.Spans.makeImmutable()
    58  }
    59  
    60  // InitSingleSpan initializes the constraint to the columns in the key context
    61  // and with one span.
    62  func (c *Constraint) InitSingleSpan(keyCtx *KeyContext, span *Span) {
    63  	c.Columns = keyCtx.Columns
    64  	c.Spans.InitSingleSpan(span)
    65  }
    66  
    67  // IsContradiction returns true if there are no spans in the constraint.
    68  func (c *Constraint) IsContradiction() bool {
    69  	return c.Spans.Count() == 0
    70  }
    71  
    72  // IsUnconstrained returns true if the constraint contains an unconstrained
    73  // span.
    74  func (c *Constraint) IsUnconstrained() bool {
    75  	return c.Spans.Count() == 1 && c.Spans.Get(0).IsUnconstrained()
    76  }
    77  
    78  // UnionWith merges the spans of the given constraint into this constraint.  The
    79  // columns of both constraints must be the same. Constrained columns in the
    80  // merged constraint can have values that are part of either of the input
    81  // constraints.
    82  func (c *Constraint) UnionWith(evalCtx *tree.EvalContext, other *Constraint) {
    83  	if !c.Columns.Equals(&other.Columns) {
    84  		panic(errors.AssertionFailedf("column mismatch"))
    85  	}
    86  	if c.IsUnconstrained() || other.IsContradiction() {
    87  		return
    88  	}
    89  
    90  	// Use variation on merge sort, because both sets of spans are ordered and
    91  	// non-overlapping.
    92  
    93  	left := &c.Spans
    94  	leftIndex := 0
    95  	right := &other.Spans
    96  	rightIndex := 0
    97  	keyCtx := MakeKeyContext(&c.Columns, evalCtx)
    98  	var result Spans
    99  	result.Alloc(left.Count() + right.Count())
   100  
   101  	for leftIndex < left.Count() || rightIndex < right.Count() {
   102  		if rightIndex < right.Count() {
   103  			if leftIndex >= left.Count() ||
   104  				left.Get(leftIndex).Compare(&keyCtx, right.Get(rightIndex)) > 0 {
   105  				// Swap the two sets, so that going forward the current left
   106  				// span starts before the current right span.
   107  				left, right = right, left
   108  				leftIndex, rightIndex = rightIndex, leftIndex
   109  			}
   110  		}
   111  
   112  		// Merge this span with any overlapping spans in left or right. Initially,
   113  		// it can only overlap with spans in right, but after the merge we can
   114  		// have new overlaps; hence why this is a loop and we check against both
   115  		// left and right. For example:
   116  		//   left : [/1 - /10] [/20 - /30] [/40 - /50]
   117  		//   right: [/5 - /25] [/30 - /40]
   118  		//                             span
   119  		//   initial:                [/1 - /10]
   120  		//   merge with [/5 - /25]:  [/1 - /25]
   121  		//   merge with [/20 - /30]: [/1 - /30]
   122  		//   merge with [/30 - /40]: [/1 - /40]
   123  		//   merge with [/40 - /50]: [/1 - /50]
   124  		//
   125  		mergeSpan := *left.Get(leftIndex)
   126  		leftIndex++
   127  		for {
   128  			// Note that Span.TryUnionWith returns false for a different reason
   129  			// than Constraint.tryUnionWith. Span.TryUnionWith returns false
   130  			// when the spans are not contiguous, and therefore the union cannot
   131  			// be represented as a valid Span. Constraint.tryUnionWith returns
   132  			// false when the merged spans are unconstrained (cover entire key
   133  			// range), and therefore the union cannot be represented as a valid
   134  			// Constraint.
   135  			var ok bool
   136  			if leftIndex < left.Count() {
   137  				if mergeSpan.TryUnionWith(&keyCtx, left.Get(leftIndex)) {
   138  					leftIndex++
   139  					ok = true
   140  				}
   141  			}
   142  			if rightIndex < right.Count() {
   143  				if mergeSpan.TryUnionWith(&keyCtx, right.Get(rightIndex)) {
   144  					rightIndex++
   145  					ok = true
   146  				}
   147  			}
   148  
   149  			// If neither union succeeded, then it means either:
   150  			//   1. The spans don't merge into a single contiguous span, and will
   151  			//      need to be represented separately in this constraint.
   152  			//   2. There are no more spans to merge.
   153  			if !ok {
   154  				break
   155  			}
   156  		}
   157  		result.Append(&mergeSpan)
   158  	}
   159  
   160  	c.Spans = result
   161  	c.Spans.makeImmutable()
   162  }
   163  
   164  // IntersectWith intersects the spans of this constraint with those in the
   165  // given constraint and updates this constraint with any overlapping spans. The
   166  // columns of both constraints must be the same. If there are no overlapping
   167  // spans, then the intersection is empty, and tryIntersectWith returns false.
   168  // If a constraint set has even one empty constraint, then the entire set
   169  // should be marked as empty and all constraints removed.
   170  func (c *Constraint) IntersectWith(evalCtx *tree.EvalContext, other *Constraint) {
   171  	if !c.Columns.Equals(&other.Columns) {
   172  		panic(errors.AssertionFailedf("column mismatch"))
   173  	}
   174  	if c.IsContradiction() || other.IsUnconstrained() {
   175  		return
   176  	}
   177  
   178  	// Use variation on merge sort, because both sets of spans are ordered and
   179  	// non-overlapping.
   180  
   181  	left := &c.Spans
   182  	leftIndex := 0
   183  	right := &other.Spans
   184  	rightIndex := 0
   185  	keyCtx := MakeKeyContext(&c.Columns, evalCtx)
   186  	var result Spans
   187  	result.Alloc(left.Count())
   188  
   189  	for leftIndex < left.Count() && rightIndex < right.Count() {
   190  		if left.Get(leftIndex).StartsAfter(&keyCtx, right.Get(rightIndex)) {
   191  			rightIndex++
   192  			continue
   193  		}
   194  
   195  		mergeSpan := *left.Get(leftIndex)
   196  		if !mergeSpan.TryIntersectWith(&keyCtx, right.Get(rightIndex)) {
   197  			leftIndex++
   198  			continue
   199  		}
   200  		result.Append(&mergeSpan)
   201  
   202  		// Skip past whichever span ends first, or skip past both if they have
   203  		// the same endpoint.
   204  		cmp := left.Get(leftIndex).CompareEnds(&keyCtx, right.Get(rightIndex))
   205  		if cmp <= 0 {
   206  			leftIndex++
   207  		}
   208  		if cmp >= 0 {
   209  			rightIndex++
   210  		}
   211  	}
   212  
   213  	c.Spans = result
   214  	c.Spans.makeImmutable()
   215  }
   216  
   217  func (c Constraint) String() string {
   218  	var b strings.Builder
   219  	b.WriteString(c.Columns.String())
   220  	b.WriteString(": ")
   221  	if c.IsUnconstrained() {
   222  		b.WriteString("unconstrained")
   223  	} else if c.IsContradiction() {
   224  		b.WriteString("contradiction")
   225  	} else {
   226  		b.WriteString(c.Spans.String())
   227  	}
   228  	return b.String()
   229  }
   230  
   231  // ContainsSpan returns true if the constraint contains the given span (or a
   232  // span that contains it).
   233  func (c *Constraint) ContainsSpan(evalCtx *tree.EvalContext, sp *Span) bool {
   234  	keyCtx := MakeKeyContext(&c.Columns, evalCtx)
   235  	// Binary search to find an overlapping span.
   236  	for l, r := 0, c.Spans.Count()-1; l <= r; {
   237  		m := (l + r) / 2
   238  		cSpan := c.Spans.Get(m)
   239  		if sp.StartsAfter(&keyCtx, cSpan) {
   240  			l = m + 1
   241  		} else if cSpan.StartsAfter(&keyCtx, sp) {
   242  			r = m - 1
   243  		} else {
   244  			// The spans must overlap. Check if sp is fully contained.
   245  			return sp.CompareStarts(&keyCtx, cSpan) >= 0 &&
   246  				sp.CompareEnds(&keyCtx, cSpan) <= 0
   247  		}
   248  	}
   249  	return false
   250  }
   251  
   252  // Combine refines the receiver constraint using constraints on a suffix of the
   253  // same list of columns. For example:
   254  //  c:      /a/b: [/1 - /2] [/4 - /4]
   255  //  other:  /b: [/5 - /5]
   256  //  result: /a/b: [/1/5 - /2/5] [/4/5 - /4/5]
   257  func (c *Constraint) Combine(evalCtx *tree.EvalContext, other *Constraint) {
   258  	if !other.Columns.IsStrictSuffixOf(&c.Columns) {
   259  		// Note: we don't want to let the c and other pointers escape by passing
   260  		// them directly to Sprintf.
   261  		panic(errors.AssertionFailedf("%s not a suffix of %s", other.String(), c.String()))
   262  	}
   263  	if c.IsUnconstrained() || c.IsContradiction() || other.IsUnconstrained() {
   264  		return
   265  	}
   266  	if other.IsContradiction() {
   267  		c.Spans = Spans{}
   268  		c.Spans.makeImmutable()
   269  		return
   270  	}
   271  	offset := c.Columns.Count() - other.Columns.Count()
   272  
   273  	var result Spans
   274  	// We only initialize result if we determine that we need to modify the list
   275  	// of spans.
   276  	var resultInitialized bool
   277  	keyCtx := KeyContext{Columns: c.Columns, EvalCtx: evalCtx}
   278  
   279  	for i := 0; i < c.Spans.Count(); i++ {
   280  		sp := *c.Spans.Get(i)
   281  
   282  		startLen, endLen := sp.start.Length(), sp.end.Length()
   283  
   284  		// Try to extend the start and end keys.
   285  		// TODO(radu): we could look at offsets in between 0 and startLen/endLen and
   286  		// potentially tighten up the spans (e.g. (a, b) > (1, 2) AND b > 10).
   287  
   288  		if startLen == endLen && startLen == offset &&
   289  			sp.start.Compare(&keyCtx, sp.end, ExtendLow, ExtendLow) == 0 {
   290  			// Special case when the start and end keys are equal (i.e. an exact value
   291  			// on this column). This is the only case where we can break up a single
   292  			// span into multiple spans. Note that this implies inclusive boundaries.
   293  			//
   294  			// For example:
   295  			//  @1 = 1 AND @2 IN (3, 4, 5)
   296  			//  constraint[0]:
   297  			//    [/1 - /1]
   298  			//  constraint[1]:
   299  			//    [/3 - /3]
   300  			//    [/4 - /4]
   301  			//    [/5 - /5]
   302  			//  We break up the span to get:
   303  			//    [/1/3 - /1/3]
   304  			//    [/1/4 - /1/4]
   305  			//    [/1/5 - /1/5]
   306  
   307  			if !resultInitialized {
   308  				resultInitialized = true
   309  				result.Alloc(c.Spans.Count() + other.Spans.Count())
   310  				for j := 0; j < i; j++ {
   311  					result.Append(c.Spans.Get(j))
   312  				}
   313  			}
   314  			for j := 0; j < other.Spans.Count(); j++ {
   315  				extSp := other.Spans.Get(j)
   316  				var newSp Span
   317  				newSp.Init(
   318  					sp.start.Concat(extSp.start), extSp.startBoundary,
   319  					sp.end.Concat(extSp.end), extSp.endBoundary,
   320  				)
   321  				result.Append(&newSp)
   322  			}
   323  			continue
   324  		}
   325  
   326  		var modified bool
   327  		if startLen == offset && sp.startBoundary == IncludeBoundary {
   328  			// We can advance the starting boundary. Calculate constraints for the
   329  			// column that follows. If we have multiple constraints, we can only use
   330  			// the start of the first one to tighten the span.
   331  			// For example:
   332  			//   @1 >= 2 AND @2 IN (1, 2, 3).
   333  			//   constraints[0]:
   334  			//     [/2 - ]
   335  			//   constraints[1]:
   336  			//     [/1 - /1]
   337  			//     [/2 - /2]
   338  			//     [/3 - /3]
   339  			//   The best we can do is tighten the span to:
   340  			//     [/2/1 - ]
   341  			extSp := other.Spans.Get(0)
   342  			if extSp.start.Length() > 0 {
   343  				sp.start = sp.start.Concat(extSp.start)
   344  				sp.startBoundary = extSp.startBoundary
   345  				modified = true
   346  			}
   347  		}
   348  		// End key case is symmetric with the one above.
   349  		if endLen == offset && sp.endBoundary == IncludeBoundary {
   350  			extSp := other.Spans.Get(other.Spans.Count() - 1)
   351  			if extSp.end.Length() > 0 {
   352  				sp.end = sp.end.Concat(extSp.end)
   353  				sp.endBoundary = extSp.endBoundary
   354  				modified = true
   355  			}
   356  		}
   357  		if modified {
   358  			// We only initialize result if we need to modify the list of spans.
   359  			if !resultInitialized {
   360  				resultInitialized = true
   361  				result.Alloc(c.Spans.Count())
   362  				for j := 0; j < i; j++ {
   363  					result.Append(c.Spans.Get(j))
   364  				}
   365  			}
   366  			// The span can become invalid (empty). For example:
   367  			//   /1/2: [/1 - /1/2]
   368  			//   /2: [/5 - /5]
   369  			// This results in an invalid span [/1/5 - /1/2] which we must discard.
   370  			if sp.start.Compare(&keyCtx, sp.end, sp.startExt(), sp.endExt()) < 0 {
   371  				result.Append(&sp)
   372  			}
   373  		} else {
   374  			if resultInitialized {
   375  				result.Append(&sp)
   376  			}
   377  		}
   378  	}
   379  	if resultInitialized {
   380  		c.Spans = result
   381  		c.Spans.makeImmutable()
   382  	}
   383  }
   384  
   385  // ConsolidateSpans merges spans that have consecutive boundaries. For example:
   386  //   [/1 - /2] [/3 - /4] becomes [/1 - /4].
   387  func (c *Constraint) ConsolidateSpans(evalCtx *tree.EvalContext) {
   388  	keyCtx := KeyContext{Columns: c.Columns, EvalCtx: evalCtx}
   389  	var result Spans
   390  	for i := 1; i < c.Spans.Count(); i++ {
   391  		last := c.Spans.Get(i - 1)
   392  		sp := c.Spans.Get(i)
   393  		if last.endBoundary == IncludeBoundary && sp.startBoundary == IncludeBoundary &&
   394  			sp.start.IsNextKey(&keyCtx, last.end) {
   395  			// We only initialize `result` if we need to change something.
   396  			if result.Count() == 0 {
   397  				result.Alloc(c.Spans.Count() - 1)
   398  				for j := 0; j < i; j++ {
   399  					result.Append(c.Spans.Get(j))
   400  				}
   401  			}
   402  			r := result.Get(result.Count() - 1)
   403  			r.end = sp.end
   404  			r.endBoundary = sp.endBoundary
   405  		} else {
   406  			if result.Count() != 0 {
   407  				result.Append(sp)
   408  			}
   409  		}
   410  	}
   411  	if result.Count() != 0 {
   412  		c.Spans = result
   413  		c.Spans.makeImmutable()
   414  	}
   415  }
   416  
   417  // ExactPrefix returns the length of the longest column prefix which are
   418  // constrained to a single value. For example:
   419  //   /a/b/c: [/1/2/3 - /1/2/3]                    ->  ExactPrefix = 3
   420  //   /a/b/c: [/1/2/3 - /1/2/3] [/1/2/5 - /1/2/8]  ->  ExactPrefix = 2
   421  //   /a/b/c: [/1/2/3 - /1/2/3] [/1/2/5 - /1/3/8]  ->  ExactPrefix = 1
   422  //   /a/b/c: [/1/2/3 - /1/2/3] [/1/3/3 - /1/3/3]  ->  ExactPrefix = 1
   423  //   /a/b/c: [/1/2/3 - /1/2/3] [/3 - /4]          ->  ExactPrefix = 0
   424  func (c *Constraint) ExactPrefix(evalCtx *tree.EvalContext) int {
   425  	if c.IsContradiction() {
   426  		return 0
   427  	}
   428  
   429  	for col := 0; ; col++ {
   430  		// Check if all spans have the same value for this column.
   431  		var val tree.Datum
   432  		for i := 0; i < c.Spans.Count(); i++ {
   433  			sp := c.Spans.Get(i)
   434  			if sp.start.Length() <= col || sp.end.Length() <= col {
   435  				return col
   436  			}
   437  			startVal := sp.start.Value(col)
   438  			if startVal.Compare(evalCtx, sp.end.Value(col)) != 0 {
   439  				return col
   440  			}
   441  			if i == 0 {
   442  				val = startVal
   443  			} else if startVal.Compare(evalCtx, val) != 0 {
   444  				return col
   445  			}
   446  		}
   447  	}
   448  }
   449  
   450  // ConstrainedColumns returns the number of columns which are constrained by
   451  // the Constraint. For example:
   452  //   /a/b/c: [/1/1 - /1] [/3 - /3]
   453  // has 2 constrained columns. This may be less than the total number of columns
   454  // in the constraint, especially if it represents an index constraint.
   455  func (c *Constraint) ConstrainedColumns(evalCtx *tree.EvalContext) int {
   456  	count := 0
   457  	for i := 0; i < c.Spans.Count(); i++ {
   458  		sp := c.Spans.Get(i)
   459  		start := sp.StartKey()
   460  		end := sp.EndKey()
   461  		if start.Length() > count {
   462  			count = start.Length()
   463  		}
   464  		if end.Length() > count {
   465  			count = end.Length()
   466  		}
   467  	}
   468  
   469  	return count
   470  }
   471  
   472  // Prefix returns the length of the longest prefix of columns for which all the
   473  // spans have the same start and end values. For example:
   474  //   /a/b/c: [/1/1/1 - /1/1/2] [/3/3/3 - /3/3/4]
   475  // has prefix 2.
   476  //
   477  // Note that Prefix returns a value that is greater than or equal to the value
   478  // returned by ExactPrefix. For example:
   479  //   /a/b/c: [/1/2/3 - /1/2/3] [/1/2/5 - /1/3/8] -> ExactPrefix = 1, Prefix = 1
   480  //   /a/b/c: [/1/2/3 - /1/2/3] [/1/3/3 - /1/3/3] -> ExactPrefix = 1, Prefix = 3
   481  func (c *Constraint) Prefix(evalCtx *tree.EvalContext) int {
   482  	prefix := 0
   483  	for ; prefix < c.Columns.Count(); prefix++ {
   484  		for i := 0; i < c.Spans.Count(); i++ {
   485  			sp := c.Spans.Get(i)
   486  			start := sp.StartKey()
   487  			end := sp.EndKey()
   488  			if start.Length() <= prefix || end.Length() <= prefix ||
   489  				start.Value(prefix).Compare(evalCtx, end.Value(prefix)) != 0 {
   490  				return prefix
   491  			}
   492  		}
   493  	}
   494  
   495  	return prefix
   496  }
   497  
   498  // ExtractConstCols returns a set of columns which are restricted to be
   499  // constant by the constraint.
   500  func (c *Constraint) ExtractConstCols(evalCtx *tree.EvalContext) opt.ColSet {
   501  	var res opt.ColSet
   502  	pre := c.ExactPrefix(evalCtx)
   503  	for i := 0; i < pre; i++ {
   504  		res.Add(c.Columns.Get(i).ID())
   505  	}
   506  	return res
   507  }
   508  
   509  // ExtractNotNullCols returns a set of columns that cannot be NULL when the
   510  // constraint holds.
   511  func (c *Constraint) ExtractNotNullCols(evalCtx *tree.EvalContext) opt.ColSet {
   512  	if c.IsUnconstrained() || c.IsContradiction() {
   513  		return opt.ColSet{}
   514  	}
   515  
   516  	var res opt.ColSet
   517  
   518  	// If we have a span where the start and end key value diverge for a column,
   519  	// none of the columns that follow can be not-null. For example:
   520  	//   /1/2/3: [/1/2/3 - /1/4/1]
   521  	// Because the span is not restricted to a single value on column 2, column 3
   522  	// can take any value, like /1/3/NULL.
   523  	//
   524  	// Find the longest prefix of columns for which all the spans have the same
   525  	// start and end values. For example:
   526  	//   [/1/1/1 - /1/1/2] [/3/3/3 - /3/3/4]
   527  	// has prefix 2. Only these columns and the first following column can be
   528  	// known to be not-null.
   529  	prefix := c.Prefix(evalCtx)
   530  	for i := 0; i < prefix; i++ {
   531  		// hasNull identifies cases like [/1/NULL/1 - /1/NULL/2].
   532  		hasNull := false
   533  		for j := 0; j < c.Spans.Count(); j++ {
   534  			start := c.Spans.Get(j).StartKey()
   535  			hasNull = hasNull || start.Value(i) == tree.DNull
   536  		}
   537  		if !hasNull {
   538  			res.Add(c.Columns.Get(i).ID())
   539  		}
   540  	}
   541  	if prefix == c.Columns.Count() {
   542  		return res
   543  	}
   544  
   545  	// Now look at the first column that follows the prefix.
   546  	col := c.Columns.Get(prefix)
   547  	for i := 0; i < c.Spans.Count(); i++ {
   548  		span := c.Spans.Get(i)
   549  		var key Key
   550  		var boundary SpanBoundary
   551  		if !col.Descending() {
   552  			key, boundary = span.StartKey(), span.StartBoundary()
   553  		} else {
   554  			key, boundary = span.EndKey(), span.EndBoundary()
   555  		}
   556  		// If the span is unbounded on the NULL side, or if it is of the form
   557  		// [/NULL - /x], the column is nullable.
   558  		if key.Length() <= prefix || (key.Value(prefix) == tree.DNull && boundary == IncludeBoundary) {
   559  			return res
   560  		}
   561  	}
   562  	// All spans constrain col to be not-null.
   563  	res.Add(col.ID())
   564  	return res
   565  }
   566  
   567  // CalculateMaxResults returns a non-zero integer indicating the maximum number
   568  // of results that can be read from indexCols by using c.Spans. The indexCols
   569  // are assumed to form at least a weak key.
   570  // If 0 is returned, the maximum number of results could not be deduced.
   571  // We can calculate the maximum number of results when both of the following
   572  // are satisfied:
   573  //  1. The index columns form a weak key (assumption), and the spans do not
   574  //     specify any nulls.
   575  //  2. All spans cover all the columns of the index and have equal start and
   576  //     end keys up to but not necessarily including the last column.
   577  // TODO(asubiotto): The only reason to extract this is that both the heuristic
   578  // planner and optimizer need this logic, due to the heuristic planner planning
   579  // mutations. Once the optimizer plans mutations, this method can go away.
   580  func (c *Constraint) CalculateMaxResults(
   581  	evalCtx *tree.EvalContext, indexCols opt.ColSet, notNullCols opt.ColSet,
   582  ) uint64 {
   583  	// Ensure that if we have nullable columns, we are only reading non-null
   584  	// values, given that a unique index allows an arbitrary number of duplicate
   585  	// entries if they have NULLs.
   586  	if !indexCols.SubsetOf(notNullCols.Union(c.ExtractNotNullCols(evalCtx))) {
   587  		return 0
   588  	}
   589  
   590  	numCols := c.Columns.Count()
   591  
   592  	// Check if the longest prefix of columns for which all the spans have the
   593  	// same start and end values covers all columns.
   594  	prefix := c.Prefix(evalCtx)
   595  	var distinctVals uint64
   596  	if prefix < numCols-1 {
   597  		return 0
   598  	} else if prefix == numCols-1 {
   599  		// If the prefix does not include the last column, calculate the number of
   600  		// distinct values possible in the span. This is only supported for int
   601  		// and date types.
   602  		for i := 0; i < c.Spans.Count(); i++ {
   603  			sp := c.Spans.Get(i)
   604  			start := sp.StartKey()
   605  			end := sp.EndKey()
   606  
   607  			// Ensure that the keys specify the last column.
   608  			if start.Length() != numCols || end.Length() != numCols {
   609  				return 0
   610  			}
   611  
   612  			// TODO(asubiotto): This logic is very similar to
   613  			// updateDistinctCountsFromConstraint. It would be nice to extract this
   614  			// logic somewhere.
   615  			colIdx := numCols - 1
   616  			startVal := start.Value(colIdx)
   617  			endVal := end.Value(colIdx)
   618  			var startIntVal, endIntVal int64
   619  			if startVal.ResolvedType().Family() == types.IntFamily &&
   620  				endVal.ResolvedType().Family() == types.IntFamily {
   621  				startIntVal = int64(*startVal.(*tree.DInt))
   622  				endIntVal = int64(*endVal.(*tree.DInt))
   623  			} else if startVal.ResolvedType().Family() == types.DateFamily &&
   624  				endVal.ResolvedType().Family() == types.DateFamily {
   625  				startDate := startVal.(*tree.DDate)
   626  				endDate := endVal.(*tree.DDate)
   627  				if !startDate.IsFinite() || !endDate.IsFinite() {
   628  					// One of the boundaries is not finite, so we can't determine the
   629  					// distinct count for this column.
   630  					return 0
   631  				}
   632  				startIntVal = int64(startDate.PGEpochDays())
   633  				endIntVal = int64(endDate.PGEpochDays())
   634  			} else {
   635  				return 0
   636  			}
   637  
   638  			if c.Columns.Get(colIdx).Ascending() {
   639  				distinctVals += uint64(endIntVal - startIntVal)
   640  			} else {
   641  				distinctVals += uint64(startIntVal - endIntVal)
   642  			}
   643  
   644  			// Add one since both start and end boundaries should be inclusive
   645  			// (due to Span.PreferInclusive).
   646  			distinctVals++
   647  		}
   648  	} else {
   649  		distinctVals = uint64(c.Spans.Count())
   650  	}
   651  	return distinctVals
   652  }