github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/constraint/constraint_set.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 "strings" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/opt" 17 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 18 "github.com/cockroachdb/errors" 19 ) 20 21 // Unconstrained is an empty constraint set which does not impose any 22 // constraints on any columns. 23 var Unconstrained = &Set{} 24 25 // Contradiction is a special constraint set which indicates there are no 26 // possible values for the expression; it will always yield the empty result 27 // set. 28 var Contradiction = &Set{contradiction: true} 29 30 // Set is a conjunction of constraints that are inferred from scalar filter 31 // conditions. The constrained expression will always evaluate to a result set 32 // having values that conform to all of the constraints in the constraint set. 33 // Each constraint within the set is a disjunction of spans that together 34 // specify the domain of possible values which that constraint's column(s) can 35 // have. See the Constraint struct comment for more details. 36 // 37 // Constraint sets are useful for selecting indexes, pruning ranges, inferring 38 // non-null columns, and more. They serve as a "summary" of arbitrarily complex 39 // expressions, so that fast decisions can be made without analyzing the entire 40 // expression tree each time. 41 // 42 // A few examples: 43 // - @1 >= 10 44 // /@1: [/10 - ] 45 // 46 // - @1 > 10 AND @2 = 5 47 // /@1: [/11 - ] 48 // /@2: [/5 - /5] 49 // 50 // - (@1 = 10 AND @2 > 5) OR (@1 = 20 AND @2 > 0) 51 // /@1: [/10 - /10] [/20 - /20] 52 // /@2: [/1 - ] 53 // 54 // - @1 > 10.5 AND @2 != 'foo' 55 // /@1: (10.5 - ] 56 // /@2: [ - 'foo') ('foo' - ] 57 // 58 type Set struct { 59 // firstConstraint holds the first constraint in the set and otherConstraints 60 // hold any constraints beyond the first. These are separated in order to 61 // optimize for the common case of a set with a single constraint. 62 firstConstraint Constraint 63 otherConstraints []Constraint 64 65 // length is the number of constraints in the set. 66 length int32 67 68 // contradiction is true if this is the special Contradiction constraint set. 69 contradiction bool 70 } 71 72 // SingleConstraint creates a Set with a single Constraint. 73 func SingleConstraint(c *Constraint) *Set { 74 if c.IsContradiction() { 75 return Contradiction 76 } 77 if c.IsUnconstrained() { 78 return Unconstrained 79 } 80 return &Set{length: 1, firstConstraint: *c} 81 } 82 83 // SingleSpanConstraint creates a Set with a single constraint which 84 // has one span. 85 func SingleSpanConstraint(keyCtx *KeyContext, span *Span) *Set { 86 if span.IsUnconstrained() { 87 return Unconstrained 88 } 89 s := &Set{length: 1} 90 s.firstConstraint.InitSingleSpan(keyCtx, span) 91 return s 92 } 93 94 // Length returns the number of constraints in the set. 95 func (s *Set) Length() int { 96 return int(s.length) 97 } 98 99 // Constraint returns the nth constraint in the set. Together with the Length 100 // method, Constraint allows iteration over the list of constraints (since 101 // there is no method to return a slice of constraints). 102 func (s *Set) Constraint(nth int) *Constraint { 103 if nth == 0 && s.length != 0 { 104 return &s.firstConstraint 105 } 106 return &s.otherConstraints[nth-1] 107 } 108 109 // IsUnconstrained returns true if the constraint set contains no constraints, 110 // which means column values can have any possible values. 111 func (s *Set) IsUnconstrained() bool { 112 return s.length == 0 && !s.contradiction 113 } 114 115 // Intersect finds the overlap between this constraint set and the given set. 116 // Constraints that exist in either of the input sets will get merged into the 117 // combined set. Compatible constraints (that share same column list) are 118 // intersected with one another. Intersect returns the merged set. 119 func (s *Set) Intersect(evalCtx *tree.EvalContext, other *Set) *Set { 120 // Intersection with the contradiction set is always the contradiction set. 121 if s == Contradiction || other == Contradiction { 122 return Contradiction 123 } 124 125 // Intersection with the unconstrained set is the identity op. 126 if s.IsUnconstrained() { 127 return other 128 } 129 if other.IsUnconstrained() { 130 return s 131 } 132 133 // Create a new set to hold the merged sets. 134 mergeSet := &Set{} 135 136 index := 0 137 length := s.Length() 138 otherIndex := 0 139 otherLength := other.Length() 140 141 // Constraints are ordered in the set by column indexes, with no duplicates, 142 // so intersection can be done as a variation on merge sort. 143 for index < length || otherIndex < otherLength { 144 // Allocate the next constraint slot in the new set. 145 merge := mergeSet.allocConstraint(length - index + otherLength - otherIndex) 146 147 var cmp int 148 if index >= length { 149 cmp = 1 150 } else if otherIndex >= otherLength { 151 cmp = -1 152 } else { 153 cmp = compareConstraintsByCols(s.Constraint(index), other.Constraint(otherIndex)) 154 } 155 156 if cmp == 0 { 157 // Constraints have same columns, so they're compatible and need to 158 // be merged. 159 *merge = *s.Constraint(index) 160 merge.IntersectWith(evalCtx, other.Constraint(otherIndex)) 161 if merge.IsContradiction() { 162 return Contradiction 163 } 164 165 // Skip past both inputs. 166 index++ 167 otherIndex++ 168 } else if cmp < 0 { 169 // This constraint has no corresponding constraint in other set, so 170 // add it to the set (absence of other constraint = unconstrained). 171 *merge = *s.Constraint(index) 172 index++ 173 } else { 174 *merge = *other.Constraint(otherIndex) 175 otherIndex++ 176 } 177 } 178 return mergeSet 179 } 180 181 // Union creates a new set with constraints that allow any value that either of 182 // the input sets allowed. Compatible constraints (that share same column list) 183 // that exist in both sets are merged with one another. Note that the results 184 // may not be "tight", meaning that the new constraint set might allow 185 // additional combinations of values that neither of the input sets allowed. For 186 // example: 187 // (x > 1 AND y > 10) OR (x < 5 AND y < 50) 188 // the union is unconstrained (and thus allows combinations like x,y = 10,0). 189 // 190 // Union returns the merged set. 191 func (s *Set) Union(evalCtx *tree.EvalContext, other *Set) *Set { 192 // Union with the contradiction set is an identity operation. 193 if s == Contradiction { 194 return other 195 } else if other == Contradiction { 196 return s 197 } 198 199 // Union with the unconstrained set yields an unconstrained set. 200 if s.IsUnconstrained() || other.IsUnconstrained() { 201 return Unconstrained 202 } 203 204 // Create a new set to hold the merged sets. 205 mergeSet := &Set{} 206 207 index := 0 208 length := s.Length() 209 otherIndex := 0 210 otherLength := other.Length() 211 212 // Constraints are ordered in the set by column indexes, with no duplicates, 213 // so union can be done as a variation on merge sort. The constraints are 214 // matched up against one another. All constraints that have a "compatible" 215 // constraint in the other set can be merged into the new set. Currently, 216 // a compatible constraint is one in which columns exactly match. 217 for index < length && otherIndex < otherLength { 218 // Skip past any constraint that does not have a corresponding 219 // constraint in the other set. A missing constraint is equivalent to 220 // a constraint that allows all values. Union of that unconstrained 221 // range with any other range is also unconstrained, and the constraint 222 // set never includes unconstrained ranges. Therefore, skipping 223 // unmatched constraints is equivalent to doing a union operation and 224 // then not adding the result to the set. 225 cmp := compareConstraintsByCols(s.Constraint(index), other.Constraint(otherIndex)) 226 if cmp < 0 { 227 index++ 228 continue 229 } else if cmp > 0 { 230 otherIndex++ 231 continue 232 } 233 234 // Constraints have same columns, so they're compatible and need to 235 // be merged. Allocate the next constraint slot in the new set. 236 merge := mergeSet.allocConstraint(length - index + otherLength - otherIndex) 237 238 *merge = *s.Constraint(index) 239 merge.UnionWith(evalCtx, other.Constraint(otherIndex)) 240 if merge.IsUnconstrained() { 241 // Together, constraints allow any possible value, and so there's nothing 242 // to add to the set. 243 mergeSet.undoAllocConstraint() 244 } 245 246 // Skip past both inputs. 247 index++ 248 otherIndex++ 249 } 250 return mergeSet 251 } 252 253 // ExtractCols returns all columns involved in the constraints in this set. 254 func (s *Set) ExtractCols() opt.ColSet { 255 var res opt.ColSet 256 if s.length == 0 { 257 return res 258 } 259 res = s.firstConstraint.Columns.ColSet() 260 for i := int32(1); i < s.length; i++ { 261 res.UnionWith(s.otherConstraints[i-1].Columns.ColSet()) 262 } 263 return res 264 } 265 266 // ExtractNotNullCols returns a set of columns that cannot be NULL for the 267 // constraints in the set to hold. 268 func (s *Set) ExtractNotNullCols(evalCtx *tree.EvalContext) opt.ColSet { 269 if s == Unconstrained || s == Contradiction { 270 return opt.ColSet{} 271 } 272 res := s.Constraint(0).ExtractNotNullCols(evalCtx) 273 for i := 1; i < s.Length(); i++ { 274 res.UnionWith(s.Constraint(i).ExtractNotNullCols(evalCtx)) 275 } 276 return res 277 } 278 279 // ExtractConstCols returns a set of columns which can only have one value 280 // for the constraints in the set to hold. 281 func (s *Set) ExtractConstCols(evalCtx *tree.EvalContext) opt.ColSet { 282 if s == Unconstrained || s == Contradiction { 283 return opt.ColSet{} 284 } 285 res := s.Constraint(0).ExtractConstCols(evalCtx) 286 for i := 1; i < s.Length(); i++ { 287 res.UnionWith(s.Constraint(i).ExtractConstCols(evalCtx)) 288 } 289 return res 290 } 291 292 // ExtractValueForConstCol extracts the value for a constant column returned 293 // by ExtractConstCols. 294 func (s *Set) ExtractValueForConstCol(evalCtx *tree.EvalContext, col opt.ColumnID) tree.Datum { 295 if s == Unconstrained || s == Contradiction { 296 return nil 297 } 298 for i := 0; i < s.Length(); i++ { 299 c := s.Constraint(i) 300 colOrd := -1 301 for j := 0; j < c.Columns.Count(); j++ { 302 if c.Columns.Get(j).ID() == col { 303 colOrd = j 304 break 305 } 306 } 307 // The column must be part of the constraint's "exact prefix". 308 if colOrd != -1 && c.ExactPrefix(evalCtx) > colOrd { 309 return c.Spans.Get(0).StartKey().Value(colOrd) 310 } 311 } 312 return nil 313 } 314 315 // IsSingleColumnConstValue returns true if the Set contains a single constraint 316 // on a single column which allows for a single constant value. On success, 317 // returns the column and the constant value. 318 func (s *Set) IsSingleColumnConstValue( 319 evalCtx *tree.EvalContext, 320 ) (col opt.ColumnID, constValue tree.Datum, ok bool) { 321 if s.Length() != 1 { 322 return 0, nil, false 323 } 324 c := s.Constraint(0) 325 if c.Columns.Count() != 1 || c.ExactPrefix(evalCtx) != 1 { 326 return 0, nil, false 327 } 328 return c.Columns.Get(0).ID(), c.Spans.Get(0).StartKey().Value(0), true 329 } 330 331 // allocConstraint allocates space for a new constraint in the set and returns 332 // a pointer to it. The first constraint is stored inline, and subsequent 333 // constraints are stored in the otherConstraints slice. 334 func (s *Set) allocConstraint(capacity int) *Constraint { 335 s.length++ 336 337 // First constraint does not require heap allocation. 338 if s.length == 1 { 339 return &s.firstConstraint 340 } 341 342 // Second constraint allocates slice. 343 if s.otherConstraints == nil { 344 s.otherConstraints = make([]Constraint, 1, capacity) 345 return &s.otherConstraints[0] 346 } 347 348 // Subsequent constraints extend slice. 349 if cap(s.otherConstraints) < capacity { 350 panic(errors.AssertionFailedf( 351 "correct capacity should have been set when otherConstraints was allocated")) 352 } 353 354 // Remember that otherConstraints' length is one less than the set length. 355 s.otherConstraints = s.otherConstraints[:s.length-1] 356 return &s.otherConstraints[s.length-2] 357 } 358 359 // undoAllocConstraint rolls back the previous allocation performed by 360 // allocConstraint. The next call to allocConstraint will allocate the same 361 // slot as before. 362 func (s *Set) undoAllocConstraint() { 363 s.length-- 364 } 365 366 func (s *Set) String() string { 367 if s.IsUnconstrained() { 368 return "unconstrained" 369 } 370 if s == Contradiction { 371 return "contradiction" 372 } 373 374 var b strings.Builder 375 for i := 0; i < s.Length(); i++ { 376 if i > 0 { 377 b.WriteString("; ") 378 } 379 b.WriteString(s.Constraint(i).String()) 380 } 381 return b.String() 382 } 383 384 // compareConstraintsByCols orders constraints by the indexes of their columns, 385 // with column position determining significance in the sort key (most 386 // significant first). 387 func compareConstraintsByCols(left, right *Constraint) int { 388 leftCount := left.Columns.Count() 389 rightCount := right.Columns.Count() 390 for i := 0; i < leftCount && i < rightCount; i++ { 391 diff := int(left.Columns.Get(i) - right.Columns.Get(i)) 392 if diff != 0 { 393 return diff 394 } 395 } 396 return leftCount - rightCount 397 }