github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/colset.go (about) 1 // Copyright 2019 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 opt 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/util" 15 "github.com/cockroachdb/errors" 16 ) 17 18 // ColSet efficiently stores an unordered set of column ids. 19 type ColSet struct { 20 set util.FastIntSet 21 } 22 23 // MakeColSet returns a set initialized with the given values. 24 func MakeColSet(vals ...ColumnID) ColSet { 25 var res ColSet 26 for _, v := range vals { 27 res.Add(v) 28 } 29 return res 30 } 31 32 // Add adds a column to the set. No-op if the column is already in the set. 33 func (s *ColSet) Add(col ColumnID) { s.set.Add(int(col)) } 34 35 // Remove removes a column from the set. No-op if the column is not in the set. 36 func (s *ColSet) Remove(col ColumnID) { s.set.Remove(int(col)) } 37 38 // Contains returns true if the set contains the column. 39 func (s ColSet) Contains(col ColumnID) bool { return s.set.Contains(int(col)) } 40 41 // Empty returns true if the set is empty. 42 func (s ColSet) Empty() bool { return s.set.Empty() } 43 44 // Len returns the number of the columns in the set. 45 func (s ColSet) Len() int { return s.set.Len() } 46 47 // Next returns the first value in the set which is >= startVal. If there is no 48 // such column, the second return value is false. 49 func (s ColSet) Next(startVal ColumnID) (ColumnID, bool) { 50 c, ok := s.set.Next(int(startVal)) 51 return ColumnID(c), ok 52 } 53 54 // ForEach calls a function for each column in the set (in increasing order). 55 func (s ColSet) ForEach(f func(col ColumnID)) { s.set.ForEach(func(i int) { f(ColumnID(i)) }) } 56 57 // Copy returns a copy of s which can be modified independently. 58 func (s ColSet) Copy() ColSet { return ColSet{set: s.set.Copy()} } 59 60 // UnionWith adds all the columns from rhs to this set. 61 func (s *ColSet) UnionWith(rhs ColSet) { s.set.UnionWith(rhs.set) } 62 63 // Union returns the union of s and rhs as a new set. 64 func (s ColSet) Union(rhs ColSet) ColSet { return ColSet{set: s.set.Union(rhs.set)} } 65 66 // IntersectionWith removes any columns not in rhs from this set. 67 func (s *ColSet) IntersectionWith(rhs ColSet) { s.set.IntersectionWith(rhs.set) } 68 69 // Intersection returns the intersection of s and rhs as a new set. 70 func (s ColSet) Intersection(rhs ColSet) ColSet { return ColSet{set: s.set.Intersection(rhs.set)} } 71 72 // DifferenceWith removes any elements in rhs from this set. 73 func (s *ColSet) DifferenceWith(rhs ColSet) { s.set.DifferenceWith(rhs.set) } 74 75 // Difference returns the elements of s that are not in rhs as a new set. 76 func (s ColSet) Difference(rhs ColSet) ColSet { return ColSet{set: s.set.Difference(rhs.set)} } 77 78 // Intersects returns true if s has any elements in common with rhs. 79 func (s ColSet) Intersects(rhs ColSet) bool { return s.set.Intersects(rhs.set) } 80 81 // Equals returns true if the two sets are identical. 82 func (s ColSet) Equals(rhs ColSet) bool { return s.set.Equals(rhs.set) } 83 84 // SubsetOf returns true if rhs contains all the elements in s. 85 func (s ColSet) SubsetOf(rhs ColSet) bool { return s.set.SubsetOf(rhs.set) } 86 87 // String returns a list representation of elements. Sequential runs of positive 88 // numbers are shown as ranges. For example, for the set {1, 2, 3 5, 6, 10}, 89 // the output is "(1-3,5,6,10)". 90 func (s ColSet) String() string { return s.set.String() } 91 92 // SingleColumn returns the single column in s. Panics if s does not contain 93 // exactly one column. 94 func (s ColSet) SingleColumn() ColumnID { 95 if s.Len() != 1 { 96 panic(errors.AssertionFailedf("expected a single column but found %d columns", s.Len())) 97 } 98 col, _ := s.Next(0) 99 return col 100 } 101 102 // TranslateColSet is used to translate a ColSet from one set of column IDs 103 // to an equivalent set. This is relevant for set operations such as UNION, 104 // INTERSECT and EXCEPT, and can be used to map a ColSet defined on the left 105 // relation to an equivalent ColSet on the right relation (or between any two 106 // relations with a defined column mapping). 107 // 108 // For example, suppose we have a UNION with the following column mapping: 109 // Left: 1, 2, 3 110 // Right: 4, 5, 6 111 // Out: 7, 8, 9 112 // 113 // Here are some possible calls to TranslateColSet and their results: 114 // TranslateColSet(ColSet{1, 2}, Left, Right) -> ColSet{4, 5} 115 // TranslateColSet(ColSet{5, 6}, Right, Out) -> ColSet{8, 9} 116 // TranslateColSet(ColSet{9}, Out, Right) -> ColSet{6} 117 // 118 // Note that for the output of TranslateColSet to be correct, colSetIn must be 119 // a subset of the columns in `from`. TranslateColSet does not check that this 120 // is the case, because that would require building a ColSet from `from`, and 121 // checking that colSetIn.SubsetOf(fromColSet) is true -- a lot of computation 122 // for a validation check. It is not correct or sufficient to check that 123 // colSetIn.Len() == colSetOut.Len(), because it is possible that colSetIn and 124 // colSetOut could have different lengths and still be valid. Consider the 125 // following case: 126 // 127 // SELECT x, x, y FROM xyz UNION SELECT a, b, c FROM abc 128 // 129 // TranslateColSet(ColSet{x, y}, Left, Right) correctly returns 130 // ColSet{a, b, c}, even though ColSet{x, y}.Len() != ColSet{a, b, c}.Len(). 131 func TranslateColSet(colSetIn ColSet, from ColList, to ColList) ColSet { 132 var colSetOut ColSet 133 for i := range from { 134 if colSetIn.Contains(from[i]) { 135 colSetOut.Add(to[i]) 136 } 137 } 138 139 return colSetOut 140 }