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  }