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 }