github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/constraint/columns.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  	"fmt"
    15  	"strings"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    18  )
    19  
    20  // Columns identifies the columns which correspond to the values in a Key (and
    21  // consequently the columns of a Span or Constraint).
    22  //
    23  // The columns have directions; a descending column inverts the order of the
    24  // values on that column (in other words, inverts the result of any Datum
    25  // comparisons on that column).
    26  type Columns struct {
    27  	// firstCol holds the first column id and otherCols hold any ids beyond the
    28  	// first. These are separated in order to optimize for the common case of a
    29  	// single-column constraint.
    30  	firstCol  opt.OrderingColumn
    31  	otherCols []opt.OrderingColumn
    32  }
    33  
    34  // Init initializes a Columns structure.
    35  func (c *Columns) Init(cols []opt.OrderingColumn) {
    36  	c.firstCol = cols[0]
    37  	c.otherCols = cols[1:]
    38  }
    39  
    40  // InitSingle is a more efficient version of Init for the common case of a
    41  // single column.
    42  func (c *Columns) InitSingle(col opt.OrderingColumn) {
    43  	c.firstCol = col
    44  	c.otherCols = nil
    45  }
    46  
    47  var _ = (*Columns).InitSingle
    48  
    49  // Count returns the number of constrained columns (always at least one).
    50  func (c *Columns) Count() int {
    51  	// There's always at least one column.
    52  	return 1 + len(c.otherCols)
    53  }
    54  
    55  // Get returns the nth column and direction. Together with the
    56  // Count method, Get allows iteration over the list of constrained
    57  // columns (since there is no method to return a slice of columns).
    58  func (c *Columns) Get(nth int) opt.OrderingColumn {
    59  	// There's always at least one column.
    60  	if nth == 0 {
    61  		return c.firstCol
    62  	}
    63  	return c.otherCols[nth-1]
    64  }
    65  
    66  // Equals returns true if the two lists of columns are identical.
    67  func (c *Columns) Equals(other *Columns) bool {
    68  	n := c.Count()
    69  	if n != other.Count() {
    70  		return false
    71  	}
    72  	if c.firstCol != other.firstCol {
    73  		return false
    74  	}
    75  	// Fast path for when the two share the same slice.
    76  	if n == 1 || &c.otherCols[0] == &other.otherCols[0] {
    77  		return true
    78  	}
    79  	// Hint for the compiler to eliminate bounds check inside the loop.
    80  	tmp := other.otherCols[:len(c.otherCols)]
    81  	for i, v := range c.otherCols {
    82  		if v != tmp[i] {
    83  			return false
    84  		}
    85  	}
    86  	return true
    87  }
    88  
    89  // IsStrictSuffixOf returns true if the columns in c are a strict suffix of the
    90  // columns in other.
    91  func (c *Columns) IsStrictSuffixOf(other *Columns) bool {
    92  	offset := other.Count() - c.Count()
    93  	if offset <= 0 {
    94  		return false
    95  	}
    96  	if c.firstCol != other.otherCols[offset-1] {
    97  		return false
    98  	}
    99  	// Fast path when the slices are aliased.
   100  	if len(c.otherCols) == 0 || &c.otherCols[0] == &other.otherCols[offset] {
   101  		return true
   102  	}
   103  	cmpCols := other.otherCols[offset:]
   104  	// Hint for the compiler to eliminate the bound check inside the loop.
   105  	cmpCols = cmpCols[:len(c.otherCols)]
   106  	for i, v := range c.otherCols {
   107  		if v != cmpCols[i] {
   108  			return false
   109  		}
   110  	}
   111  	return true
   112  }
   113  
   114  // ColSet returns the columns as a ColSet.
   115  func (c *Columns) ColSet() opt.ColSet {
   116  	var r opt.ColSet
   117  	r.Add(c.firstCol.ID())
   118  	for _, c := range c.otherCols {
   119  		r.Add(c.ID())
   120  	}
   121  	return r
   122  }
   123  
   124  func (c Columns) String() string {
   125  	var b strings.Builder
   126  
   127  	for i := 0; i < c.Count(); i++ {
   128  		b.WriteRune('/')
   129  		b.WriteString(fmt.Sprintf("%d", c.Get(i)))
   130  	}
   131  	return b.String()
   132  }