github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/constraint/span.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  	"bytes"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    17  	"github.com/cockroachdb/errors"
    18  )
    19  
    20  // SpanBoundary specifies whether a span endpoint is inclusive or exclusive of
    21  // its start or end key. An inclusive boundary is represented as '[' and an
    22  // exclusive boundary is represented as ')'. Examples:
    23  //   [/0 - /1]  (inclusive, inclusive)
    24  //   [/1 - /10) (inclusive, exclusive)
    25  type SpanBoundary bool
    26  
    27  const (
    28  	// IncludeBoundary indicates that the boundary does include the respective
    29  	// key.
    30  	IncludeBoundary SpanBoundary = false
    31  
    32  	// ExcludeBoundary indicates that the boundary does not include the
    33  	// respective key.
    34  	ExcludeBoundary SpanBoundary = true
    35  )
    36  
    37  // Span represents the range between two composite keys. The end keys of the
    38  // range can be inclusive or exclusive. Each key value within the range is
    39  // an N-tuple of datum values, one for each constrained column. Here are some
    40  // examples:
    41  //   @1 < 100                                          : [ - /100)
    42  //   @1 >= 100                                         : [/100 - ]
    43  //   @1 >= 1 AND @1 <= 10                              : [/1 - /10]
    44  //   (@1 = 100 AND @2 > 10) OR (@1 > 100 AND @1 <= 101): (/100/10 - /101]
    45  type Span struct {
    46  	// Start is the starting boundary for the span.
    47  	start Key
    48  
    49  	// End is the ending boundary for the span.
    50  	end Key
    51  
    52  	// startBoundary indicates whether the span contains the start key value.
    53  	startBoundary SpanBoundary
    54  
    55  	// endBoundary indicates whether the span contains the the end key value.
    56  	endBoundary SpanBoundary
    57  }
    58  
    59  // UnconstrainedSpan is the span without any boundaries.
    60  var UnconstrainedSpan = Span{}
    61  
    62  // IsUnconstrained is true if the span does not constrain the key range. Both
    63  // the start and end boundaries are empty. This is the default state of a Span
    64  // before Set is called. Unconstrained spans cannot be used in constraints,
    65  // since the absence of a constraint is equivalent to an unconstrained span.
    66  func (sp *Span) IsUnconstrained() bool {
    67  	startUnconstrained := sp.start.IsEmpty() || (sp.start.IsNull() && sp.startBoundary == IncludeBoundary)
    68  	endUnconstrained := sp.end.IsEmpty()
    69  
    70  	return startUnconstrained && endUnconstrained
    71  }
    72  
    73  // HasSingleKey is true if the span contains exactly one key. This is true when
    74  // the start key is the same as the end key, and both boundaries are inclusive.
    75  func (sp *Span) HasSingleKey(evalCtx *tree.EvalContext) bool {
    76  	l := sp.start.Length()
    77  	if l == 0 || l != sp.end.Length() {
    78  		return false
    79  	}
    80  	if sp.startBoundary != IncludeBoundary || sp.endBoundary != IncludeBoundary {
    81  		return false
    82  	}
    83  	for i, n := 0, l; i < n; i++ {
    84  		if sp.start.Value(i).Compare(evalCtx, sp.end.Value(i)) != 0 {
    85  			return false
    86  		}
    87  	}
    88  	return true
    89  }
    90  
    91  // StartKey returns the start key.
    92  func (sp *Span) StartKey() Key {
    93  	return sp.start
    94  }
    95  
    96  // StartBoundary returns whether the start key is included or excluded.
    97  func (sp *Span) StartBoundary() SpanBoundary {
    98  	return sp.startBoundary
    99  }
   100  
   101  // EndKey returns the end key.
   102  func (sp *Span) EndKey() Key {
   103  	return sp.end
   104  }
   105  
   106  // EndBoundary returns whether the end key is included or excluded.
   107  func (sp *Span) EndBoundary() SpanBoundary {
   108  	return sp.endBoundary
   109  }
   110  
   111  // Init sets the boundaries of this span to the given values. The following
   112  // spans are not allowed:
   113  //  1. Empty span (should never be used in a constraint); not verified.
   114  //  2. Exclusive empty key boundary (use inclusive instead); causes panic.
   115  func (sp *Span) Init(start Key, startBoundary SpanBoundary, end Key, endBoundary SpanBoundary) {
   116  	if start.IsEmpty() && startBoundary == ExcludeBoundary {
   117  		// Enforce one representation for empty boundary.
   118  		panic(errors.AssertionFailedf("an empty start boundary must be inclusive"))
   119  	}
   120  	if end.IsEmpty() && endBoundary == ExcludeBoundary {
   121  		// Enforce one representation for empty boundary.
   122  		panic(errors.AssertionFailedf("an empty end boundary must be inclusive"))
   123  	}
   124  
   125  	sp.start = start
   126  	sp.startBoundary = startBoundary
   127  	sp.end = end
   128  	sp.endBoundary = endBoundary
   129  }
   130  
   131  // Compare returns an integer indicating the ordering of the two spans. The
   132  // result will be 0 if the spans are equal, -1 if this span is less than the
   133  // given span, or 1 if this span is greater. Spans are first compared based on
   134  // their start boundaries. If those are equal, then their end boundaries are
   135  // compared. An inclusive start boundary is less than an exclusive start
   136  // boundary, and an exclusive end boundary is less than an inclusive end
   137  // boundary. Here are examples of how various spans are ordered, with
   138  // equivalent extended keys shown as well (see Key.Compare comment):
   139  //   [     - /2  )  =  /Low      - /2/Low
   140  //   [     - /2/1)  =  /Low      - /2/1/Low
   141  //   [     - /2/1]  =  /Low      - /2/1/High
   142  //   [     - /2  ]  =  /Low      - /2/High
   143  //   [     -     ]  =  /Low      - /High
   144  //   [/1   - /2/1)  =  /1/Low    - /2/1/Low
   145  //   [/1   - /2/1]  =  /1/Low    - /2/1/High
   146  //   [/1   -     ]  =  /1/Low    - /High
   147  //   [/1/1 - /2  )  =  /1/1/Low  - /2/Low
   148  //   [/1/1 - /2  ]  =  /1/1/Low  - /2/High
   149  //   [/1/1 -     ]  =  /1/1/Low  - /High
   150  //   (/1/1 - /2  )  =  /1/1/High - /2/Low
   151  //   (/1/1 - /2  ]  =  /1/1/High - /2/High
   152  //   (/1/1 -     ]  =  /1/1/High - /High
   153  //   (/1   - /2/1)  =  /1/High   - /2/1/Low
   154  //   (/1   - /2/1]  =  /1/High   - /2/1/High
   155  //   (/1   -     ]  =  /1/High   - /High
   156  func (sp *Span) Compare(keyCtx *KeyContext, other *Span) int {
   157  	// Span with lowest start boundary is less than the other.
   158  	if cmp := sp.CompareStarts(keyCtx, other); cmp != 0 {
   159  		return cmp
   160  	}
   161  
   162  	// Start boundary is same, so span with lowest end boundary is less than
   163  	// the other.
   164  	if cmp := sp.CompareEnds(keyCtx, other); cmp != 0 {
   165  		return cmp
   166  	}
   167  
   168  	// End boundary is same as well, so spans are the same.
   169  	return 0
   170  }
   171  
   172  // CompareStarts returns an integer indicating the ordering of the start
   173  // boundaries of the two spans. The result will be 0 if the spans have the same
   174  // start boundary, -1 if this span has a smaller start boundary than the given
   175  // span, or 1 if this span has a bigger start boundary than the given span.
   176  func (sp *Span) CompareStarts(keyCtx *KeyContext, other *Span) int {
   177  	return sp.start.Compare(keyCtx, other.start, sp.startExt(), other.startExt())
   178  }
   179  
   180  // CompareEnds returns an integer indicating the ordering of the end boundaries
   181  // of the two spans. The result will be 0 if the spans have the same end
   182  // boundary, -1 if this span has a smaller end boundary than the given span, or
   183  // 1 if this span has a bigger end boundary than the given span.
   184  func (sp *Span) CompareEnds(keyCtx *KeyContext, other *Span) int {
   185  	return sp.end.Compare(keyCtx, other.end, sp.endExt(), other.endExt())
   186  }
   187  
   188  // StartsAfter returns true if this span is greater than the given span and
   189  // does not overlap it. In other words, this span's start boundary is greater
   190  // or equal to the given span's end boundary.
   191  func (sp *Span) StartsAfter(keyCtx *KeyContext, other *Span) bool {
   192  	return sp.start.Compare(keyCtx, other.end, sp.startExt(), other.endExt()) >= 0
   193  }
   194  
   195  // StartsStrictlyAfter returns true if this span is greater than the given span and
   196  // does not overlap or touch it. In other words, this span's start boundary is
   197  // strictly greater than the given span's end boundary.
   198  func (sp *Span) StartsStrictlyAfter(keyCtx *KeyContext, other *Span) bool {
   199  	return sp.start.Compare(keyCtx, other.end, sp.startExt(), other.endExt()) > 0
   200  }
   201  
   202  // TryIntersectWith finds the overlap between this span and the given span.
   203  // This span is updated to only cover the range that is common to both spans.
   204  // If there is no overlap, then this span will not be updated, and
   205  // TryIntersectWith will return false.
   206  func (sp *Span) TryIntersectWith(keyCtx *KeyContext, other *Span) bool {
   207  	cmpStarts := sp.CompareStarts(keyCtx, other)
   208  	if cmpStarts > 0 {
   209  		// If this span's start boundary is >= the other span's end boundary,
   210  		// then intersection is empty.
   211  		if sp.start.Compare(keyCtx, other.end, sp.startExt(), other.endExt()) >= 0 {
   212  			return false
   213  		}
   214  	}
   215  
   216  	cmpEnds := sp.CompareEnds(keyCtx, other)
   217  	if cmpEnds < 0 {
   218  		// If this span's end boundary is <= the other span's start boundary,
   219  		// then intersection is empty.
   220  		if sp.end.Compare(keyCtx, other.start, sp.endExt(), other.startExt()) <= 0 {
   221  			return false
   222  		}
   223  	}
   224  
   225  	// Only update now that it's known that intersection is not empty.
   226  	if cmpStarts < 0 {
   227  		sp.start = other.start
   228  		sp.startBoundary = other.startBoundary
   229  	}
   230  	if cmpEnds > 0 {
   231  		sp.end = other.end
   232  		sp.endBoundary = other.endBoundary
   233  	}
   234  	return true
   235  }
   236  
   237  // TryUnionWith attempts to merge this span with the given span. If the merged
   238  // spans cannot be expressed as a single span, then TryUnionWith will not
   239  // update the span and TryUnionWith returns false. This could occur if the
   240  // spans are disjoint, for example:
   241  //   [/1 - /5] UNION [/10 - /15]
   242  //
   243  // Otherwise, this span is updated to the merged span range and TryUnionWith
   244  // returns true. If the resulting span does not constrain the range [ - ], then
   245  // its IsUnconstrained method returns true, and it cannot be used as part of a
   246  // constraint in a constraint set.
   247  func (sp *Span) TryUnionWith(keyCtx *KeyContext, other *Span) bool {
   248  	// Determine the minimum start boundary.
   249  	cmpStartKeys := sp.CompareStarts(keyCtx, other)
   250  
   251  	var cmp int
   252  	if cmpStartKeys < 0 {
   253  		// This span is less, so see if there's any "space" after it and before
   254  		// the start of the other span.
   255  		cmp = sp.end.Compare(keyCtx, other.start, sp.endExt(), other.startExt())
   256  	} else if cmpStartKeys > 0 {
   257  		// This span is greater, so see if there's any "space" before it and
   258  		// after the end of the other span.
   259  		cmp = other.end.Compare(keyCtx, sp.start, other.endExt(), sp.startExt())
   260  	}
   261  	if cmp < 0 {
   262  		// There's "space" between spans, so union of these spans can't be
   263  		// expressed as a single span.
   264  		return false
   265  	}
   266  
   267  	// Determine the maximum end boundary.
   268  	cmpEndKeys := sp.CompareEnds(keyCtx, other)
   269  
   270  	// Create the merged span.
   271  	if cmpStartKeys > 0 {
   272  		sp.start = other.start
   273  		sp.startBoundary = other.startBoundary
   274  	}
   275  	if cmpEndKeys < 0 {
   276  		sp.end = other.end
   277  		sp.endBoundary = other.endBoundary
   278  	}
   279  	return true
   280  }
   281  
   282  // PreferInclusive tries to convert exclusive keys to inclusive keys. This is
   283  // only possible if the relevant type supports Next/Prev.
   284  //
   285  // We prefer inclusive constraints because we can extend inclusive constraints
   286  // with more constraints on columns that follow.
   287  //
   288  // Examples:
   289  //  - for an integer column (/1 - /5)  =>  [/2 - /4].
   290  //  - for a descending integer column (/5 - /1) => (/4 - /2).
   291  //  - for a string column, we don't have Prev so
   292  //      (/foo - /qux)  =>  [/foo\x00 - /qux).
   293  //  - for a decimal column, we don't have either Next or Prev so we can't
   294  //    change anything.
   295  func (sp *Span) PreferInclusive(keyCtx *KeyContext) {
   296  	if sp.startBoundary == ExcludeBoundary {
   297  		if key, ok := sp.start.Next(keyCtx); ok {
   298  			sp.start = key
   299  			sp.startBoundary = IncludeBoundary
   300  		}
   301  	}
   302  	if sp.endBoundary == ExcludeBoundary {
   303  		if key, ok := sp.end.Prev(keyCtx); ok {
   304  			sp.end = key
   305  			sp.endBoundary = IncludeBoundary
   306  		}
   307  	}
   308  }
   309  
   310  // CutFront removes the first numCols columns in both keys.
   311  func (sp *Span) CutFront(numCols int) {
   312  	sp.start = sp.start.CutFront(numCols)
   313  	sp.end = sp.end.CutFront(numCols)
   314  }
   315  
   316  func (sp *Span) startExt() KeyExtension {
   317  	// Trivial cast of start boundary value:
   318  	//   IncludeBoundary (false) = ExtendLow (false)
   319  	//   ExcludeBoundary (true)  = ExtendHigh (true)
   320  	return KeyExtension(sp.startBoundary)
   321  }
   322  
   323  func (sp *Span) endExt() KeyExtension {
   324  	// Invert end boundary value:
   325  	//   IncludeBoundary (false) = ExtendHigh (true)
   326  	//   ExcludeBoundary (true)  = ExtendLow (false)
   327  	return KeyExtension(!sp.endBoundary)
   328  }
   329  
   330  // String formats a Span. Inclusivity/exclusivity is shown using
   331  // brackets/parens. Some examples:
   332  //   [1 - 2]
   333  //   (1/1 - 2)
   334  //   [ - 5/6)
   335  //   [1 - ]
   336  //   [ - ]
   337  func (sp Span) String() string {
   338  	var buf bytes.Buffer
   339  	if sp.startBoundary == IncludeBoundary {
   340  		buf.WriteRune('[')
   341  	} else {
   342  		buf.WriteRune('(')
   343  	}
   344  
   345  	buf.WriteString(sp.start.String())
   346  	buf.WriteString(" - ")
   347  	buf.WriteString(sp.end.String())
   348  
   349  	if sp.endBoundary == IncludeBoundary {
   350  		buf.WriteRune(']')
   351  	} else {
   352  		buf.WriteRune(')')
   353  	}
   354  
   355  	return buf.String()
   356  }