github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/ordering.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 opt 12 13 import ( 14 "bytes" 15 "fmt" 16 17 "github.com/cockroachdb/errors" 18 ) 19 20 // OrderingColumn is the ColumnID for a column that is part of an ordering, 21 // except that it can be negated to indicate a descending ordering on that 22 // column. 23 type OrderingColumn int32 24 25 // MakeOrderingColumn initializes an ordering column with a ColumnID and a flag 26 // indicating whether the direction is descending. 27 func MakeOrderingColumn(id ColumnID, descending bool) OrderingColumn { 28 if descending { 29 return OrderingColumn(-id) 30 } 31 return OrderingColumn(id) 32 } 33 34 // ID returns the ColumnID for this OrderingColumn. 35 func (c OrderingColumn) ID() ColumnID { 36 if c < 0 { 37 return ColumnID(-c) 38 } 39 return ColumnID(c) 40 } 41 42 // Ascending returns true if the ordering on this column is ascending. 43 func (c OrderingColumn) Ascending() bool { 44 return c > 0 45 } 46 47 // Descending returns true if the ordering on this column is descending. 48 func (c OrderingColumn) Descending() bool { 49 return c < 0 50 } 51 52 func (c OrderingColumn) String() string { 53 var buf bytes.Buffer 54 c.Format(&buf) 55 return buf.String() 56 } 57 58 // Format prints a string representation to the buffer. 59 func (c OrderingColumn) Format(buf *bytes.Buffer) { 60 if c.Descending() { 61 buf.WriteByte('-') 62 } else { 63 buf.WriteByte('+') 64 } 65 fmt.Fprintf(buf, "%d", c.ID()) 66 } 67 68 // Ordering defines the order of rows provided or required by an operator. A 69 // negative value indicates descending order on the column id "-(value)". 70 type Ordering []OrderingColumn 71 72 // Empty returns true if the ordering is empty or unset. 73 func (o Ordering) Empty() bool { 74 return len(o) == 0 75 } 76 77 func (o Ordering) String() string { 78 var buf bytes.Buffer 79 o.Format(&buf) 80 return buf.String() 81 } 82 83 // Format prints a string representation to the buffer. 84 func (o Ordering) Format(buf *bytes.Buffer) { 85 for i, col := range o { 86 if i > 0 { 87 buf.WriteString(",") 88 } 89 col.Format(buf) 90 } 91 } 92 93 // ColSet returns the set of column IDs used in the ordering. 94 func (o Ordering) ColSet() ColSet { 95 var colSet ColSet 96 for _, col := range o { 97 colSet.Add(col.ID()) 98 } 99 return colSet 100 } 101 102 // Provides returns true if the required ordering is a prefix of this ordering. 103 func (o Ordering) Provides(required Ordering) bool { 104 if len(o) < len(required) { 105 return false 106 } 107 108 for i := range required { 109 if o[i] != required[i] { 110 return false 111 } 112 } 113 return true 114 } 115 116 // CommonPrefix returns the longest ordering that is a prefix of both orderings. 117 func (o Ordering) CommonPrefix(other Ordering) Ordering { 118 for i := range o { 119 if i >= len(other) || o[i] != other[i] { 120 return o[:i] 121 } 122 } 123 return o 124 } 125 126 // Equals returns true if the two orderings are identical. 127 func (o Ordering) Equals(rhs Ordering) bool { 128 if len(o) != len(rhs) { 129 return false 130 } 131 132 for i := range o { 133 if o[i] != rhs[i] { 134 return false 135 } 136 } 137 return true 138 } 139 140 // OrderingSet is a set of orderings, with the restriction that no ordering 141 // is a prefix of another ordering in the set. 142 type OrderingSet []Ordering 143 144 // Copy returns a copy of the set which can be independently modified. 145 func (os OrderingSet) Copy() OrderingSet { 146 res := make(OrderingSet, len(os)) 147 copy(res, os) 148 return res 149 } 150 151 // Add an ordering to the list, checking whether it is a prefix of another 152 // ordering (or vice-versa). 153 func (os *OrderingSet) Add(o Ordering) { 154 if len(o) == 0 { 155 panic(errors.AssertionFailedf("empty ordering")) 156 } 157 for i := range *os { 158 prefix := (*os)[i].CommonPrefix(o) 159 if len(prefix) == len(o) { 160 // o is equal to, or a prefix of os[i]. Do nothing. 161 return 162 } 163 if len(prefix) == len((*os)[i]) { 164 // os[i] is a prefix of o; replace it. 165 (*os)[i] = o 166 return 167 } 168 } 169 *os = append(*os, o) 170 } 171 172 // RestrictToPrefix keeps only the orderings that have the required ordering as 173 // a prefix. 174 func (os *OrderingSet) RestrictToPrefix(required Ordering) { 175 res := (*os)[:0] 176 for _, o := range *os { 177 if o.Provides(required) { 178 res = append(res, o) 179 } 180 } 181 *os = res 182 } 183 184 // RestrictToCols keeps only the orderings (or prefixes of them) that refer to 185 // columns in the given set. 186 func (os *OrderingSet) RestrictToCols(cols ColSet) { 187 old := *os 188 *os = old[:0] 189 for _, o := range old { 190 // Find the longest prefix of the ordering that contains 191 // only columns in the set. 192 prefix := 0 193 for _, c := range o { 194 if !cols.Contains(c.ID()) { 195 break 196 } 197 prefix++ 198 } 199 if prefix > 0 { 200 // This function appends at most one element; it is ok to operate on the 201 // same slice. 202 os.Add(o[:prefix]) 203 } 204 } 205 } 206 207 func (os OrderingSet) String() string { 208 var buf bytes.Buffer 209 for i, o := range os { 210 if i > 0 { 211 buf.WriteByte(' ') 212 } 213 buf.WriteByte('(') 214 buf.WriteString(o.String()) 215 buf.WriteByte(')') 216 } 217 return buf.String() 218 }