github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/props/physical/ordering_choice.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 physical 12 13 import ( 14 "bytes" 15 "fmt" 16 "regexp" 17 "strconv" 18 "strings" 19 "sync" 20 21 "github.com/cockroachdb/cockroach/pkg/sql/opt" 22 "github.com/cockroachdb/cockroach/pkg/sql/opt/props" 23 "github.com/cockroachdb/errors" 24 ) 25 26 // OrderingChoice defines the set of possible row orderings that are provided or 27 // required by an operator. An OrderingChoice consists of two parts: an ordered 28 // sequence of equivalent column groups and a set of optional columns. Together, 29 // these parts specify a simple pattern that can match one or more candidate 30 // orderings. Here are some examples: 31 // 32 // +1 ORDER BY a 33 // +1,-2 ORDER BY a,b DESC 34 // +(1|2) ORDER BY a | ORDER BY b 35 // +(1|2),+3 ORDER BY a,c | ORDER BY b, c 36 // -(3|4),+5 opt(1,2) ORDER BY c DESC,e | ORDER BY a,d DESC,b DESC,e | ... 37 // 38 // Each column in the ordering sequence forms the corresponding column of the 39 // sort key, from most significant to least significant. Each column has a sort 40 // direction, either ascending or descending. The relation is ordered by the 41 // first column; rows that have the same value are then ordered by the second 42 // column; rows that still have the same value are ordered by the third column, 43 // and so on. 44 // 45 // Sometimes multiple columns in the relation have equivalent values. The 46 // OrderingChoiceColumn stores these columns in a group; any of the columns in 47 // the group can be used to form the corresponding column in the sort key. The 48 // equivalent group columns come from SQL expressions like: 49 // 50 // a=b 51 // 52 // The optional column set contains columns that can appear anywhere (or 53 // nowhere) in the ordering. Optional columns come from SQL expressions like: 54 // 55 // a=1 56 // 57 // Another case for optional columns is when we are grouping along a set of 58 // columns and only care about the intra-group ordering. 59 // 60 // The optional columns can be interleaved anywhere in the sequence of ordering 61 // columns, as they have no effect on the ordering. 62 type OrderingChoice struct { 63 // Optional is the set of columns that can appear at any position in the 64 // ordering. Columns in Optional must not appear in the Columns sequence. 65 // In addition, if Columns is empty, then Optional must be as well. 66 // After initial construction, Optional is immutable. To update, replace 67 // with a different set containing the desired columns. 68 Optional opt.ColSet 69 70 // Columns is the sequence of equivalent column groups that can be used to 71 // form each column in the sort key. Columns must not appear in the Optional 72 // set. The array memory is owned by this struct, and should not be copied 73 // to another OrderingChoice unless both are kept immutable. 74 Columns []OrderingColumnChoice 75 } 76 77 // OrderingColumnChoice specifies the set of columns which can form one of the 78 // columns in the sort key, as well as the direction of that column (ascending 79 // or descending). 80 type OrderingColumnChoice struct { 81 // Group is a set of equivalent columns, any of which can be used to form a 82 // column in the sort key. After initial construction, Group is immutable. 83 // To update, replace with a different set containing the desired columns. 84 Group opt.ColSet 85 86 // Descending is true if the sort key column is ordered from highest to 87 // lowest. Otherwise, it's ordered from lowest to highest. 88 Descending bool 89 } 90 91 const ( 92 colChoiceRegexStr = `(?:\((\d+(?:\|\d+)*)\))` 93 ordColRegexStr = `^(?:(?:\+|\-)(?:(\d+)|` + colChoiceRegexStr + `))$` 94 colListRegexStr = `(\d+(?:,\d+)*)` 95 optRegexStr = `^\s*([\S]+)?\s*(?:opt\(` + colListRegexStr + `\))?\s*$` 96 ) 97 98 var once sync.Once 99 var optRegex, ordColRegex *regexp.Regexp 100 101 // ParseOrderingChoice parses the string representation of an OrderingChoice for 102 // testing purposes. Here are some examples of the string format: 103 // 104 // +1 105 // -(1|2),+3 106 // +(1|2),+3 opt(5,6) 107 // 108 // The input string is expected to be valid; ParseOrderingChoice will panic if 109 // it is not. 110 func ParseOrderingChoice(s string) OrderingChoice { 111 once.Do(func() { 112 optRegex = regexp.MustCompile(optRegexStr) 113 ordColRegex = regexp.MustCompile(ordColRegexStr) 114 }) 115 116 var ordering OrderingChoice 117 118 // Separate string into column sequence and optional column parts: 119 // +(1|2),+3 opt(5,6) 120 // matches[1]: +(1|2),+3 121 // matches[2]: opt(5,6) 122 matches := optRegex.FindStringSubmatch(s) 123 if matches == nil { 124 panic(errors.AssertionFailedf("could not parse ordering choice: %s", s)) 125 } 126 127 // Handle Any case. 128 if len(matches[1]) == 0 { 129 return OrderingChoice{} 130 } 131 132 // Split column sequence by comma: 133 // +(1|2),+3: 134 // +(1|2) 135 // +3 136 for _, ordColStr := range strings.Split(matches[1], ",") { 137 // Parse one item in the column sequence: 138 // +(1|2): 139 // matches[1]: <empty> 140 // matches[2]: 1|2 141 // 142 // +3: 143 // matches[1]: 3 144 // matches[2]: <empty> 145 ordColMatches := ordColRegex.FindStringSubmatch(ordColStr) 146 147 // First character is the direction indicator. 148 var colChoice OrderingColumnChoice 149 colChoice.Descending = strings.HasPrefix(ordColStr, "-") 150 151 if len(ordColMatches[1]) != 0 { 152 // Single column in equivalence group. 153 id, _ := strconv.Atoi(ordColMatches[1]) 154 colChoice.Group.Add(opt.ColumnID(id)) 155 } else { 156 // Split multiple columns in equivalence group by pipe: 157 // 1|2: 158 // 1 159 // 2 160 for _, idStr := range strings.Split(ordColMatches[2], "|") { 161 id, _ := strconv.Atoi(idStr) 162 colChoice.Group.Add(opt.ColumnID(id)) 163 } 164 } 165 166 ordering.Columns = append(ordering.Columns, colChoice) 167 } 168 169 // Parse any optional columns by splitting by comma: 170 // opt(5,6): 171 // 5 172 // 6 173 if len(matches[2]) != 0 { 174 for _, idStr := range strings.Split(matches[2], ",") { 175 id, _ := strconv.Atoi(idStr) 176 ordering.Optional.Add(opt.ColumnID(id)) 177 } 178 } 179 180 return ordering 181 } 182 183 // ParseOrdering parses a simple opt.Ordering; for example: "+1,-3". 184 // 185 // The input string is expected to be valid; ParseOrdering will panic if it is 186 // not. 187 func ParseOrdering(str string) opt.Ordering { 188 prov := ParseOrderingChoice(str) 189 if !prov.Optional.Empty() { 190 panic(errors.AssertionFailedf("invalid ordering %s", str)) 191 } 192 for i := range prov.Columns { 193 if prov.Columns[i].Group.Len() != 1 { 194 panic(errors.AssertionFailedf("invalid ordering %s", str)) 195 } 196 } 197 return prov.ToOrdering() 198 } 199 200 // Any is true if this instance allows any ordering (any length, any columns). 201 func (oc *OrderingChoice) Any() bool { 202 return len(oc.Columns) == 0 203 } 204 205 // FromOrdering sets this OrderingChoice to the given opt.Ordering. 206 func (oc *OrderingChoice) FromOrdering(ord opt.Ordering) { 207 oc.Optional = opt.ColSet{} 208 oc.Columns = make([]OrderingColumnChoice, len(ord)) 209 for i := range ord { 210 oc.Columns[i].Group.Add(ord[i].ID()) 211 oc.Columns[i].Descending = ord[i].Descending() 212 } 213 } 214 215 // FromOrderingWithOptCols sets this OrderingChoice to the given opt.Ordering 216 // and with the given optional columns. Any optional columns in the given 217 // ordering are ignored. 218 func (oc *OrderingChoice) FromOrderingWithOptCols(ord opt.Ordering, optCols opt.ColSet) { 219 oc.Optional = optCols.Copy() 220 oc.Columns = make([]OrderingColumnChoice, 0, len(ord)) 221 for i := range ord { 222 if !oc.Optional.Contains(ord[i].ID()) { 223 oc.Columns = append(oc.Columns, OrderingColumnChoice{ 224 Group: opt.MakeColSet(ord[i].ID()), 225 Descending: ord[i].Descending(), 226 }) 227 } 228 } 229 } 230 231 // ToOrdering returns an opt.Ordering instance composed of the shortest possible 232 // orderings that this instance allows. If there are several, then one is chosen 233 // arbitrarily. 234 func (oc *OrderingChoice) ToOrdering() opt.Ordering { 235 ordering := make(opt.Ordering, len(oc.Columns)) 236 for i := range oc.Columns { 237 col := &oc.Columns[i] 238 ordering[i] = opt.MakeOrderingColumn(col.AnyID(), col.Descending) 239 } 240 return ordering 241 } 242 243 // ColSet returns the set of all non-optional columns that are part of this 244 // instance. For example, (1,2,3) will be returned if the OrderingChoice is: 245 // 246 // +1,(2|3) opt(4,5) 247 // 248 func (oc *OrderingChoice) ColSet() opt.ColSet { 249 var cs opt.ColSet 250 for i := range oc.Columns { 251 cs.UnionWith(oc.Columns[i].Group) 252 } 253 return cs 254 } 255 256 // Implies returns true if any ordering allowed by <oc> is also allowed by <other>. 257 // 258 // In the case of no optional or equivalent columns, Implies returns true when 259 // the given ordering is a prefix of this ordering. 260 // 261 // Examples: 262 // 263 // <empty> implies <empty> 264 // +1 implies <empty> (given set is prefix) 265 // +1 implies +1 266 // +1,-2 implies +1 (given set is prefix) 267 // +1,-2 implies +1,-2 268 // +1 implies +1 opt(2) (unused optional col is ignored) 269 // -2,+1 implies +1 opt(2) (optional col is ignored) 270 // +1 implies +(1|2) (subset of choice) 271 // +(1|2) implies +(1|2|3) (subset of choice) 272 // +(1|2),-4 implies +(1|2|3),-(4|5) 273 // +(1|2) opt(4) implies +(1|2|3) opt(4) 274 // 275 // <empty> !implies +1 276 // +1 !implies -1 (direction mismatch) 277 // +1 !implies +1,-2 (prefix matching not commutative) 278 // +1 opt(2) !implies +1 (extra optional cols not allowed) 279 // +1 opt(2) !implies +1 opt(3) 280 // +(1|2) !implies -(1|2) (direction mismatch) 281 // +(1|2) !implies +(3|4) (no intersection) 282 // +(1|2) !implies +(2|3) (intersects, but not subset) 283 // +(1|2|3) !implies +(1|2) (subset of choice not commutative) 284 // +(1|2) !implies +1 opt(2) 285 // 286 func (oc *OrderingChoice) Implies(other *OrderingChoice) bool { 287 if !oc.Optional.SubsetOf(other.Optional) { 288 return false 289 } 290 291 for left, right := 0, 0; right < len(other.Columns); { 292 if left >= len(oc.Columns) { 293 return false 294 } 295 296 leftCol, rightCol := &oc.Columns[left], &other.Columns[right] 297 298 switch { 299 case leftCol.Descending == rightCol.Descending && leftCol.Group.SubsetOf(rightCol.Group): 300 // The columns match. 301 left, right = left+1, right+1 302 303 case leftCol.Group.Intersects(other.Optional): 304 // Left column is optional in the right set. 305 left++ 306 307 default: 308 return false 309 } 310 } 311 return true 312 } 313 314 // Intersects returns true if there are orderings that satisfy both 315 // OrderingChoices. See Intersection for more information. 316 func (oc *OrderingChoice) Intersects(other *OrderingChoice) bool { 317 for left, right := 0, 0; left < len(oc.Columns) && right < len(other.Columns); { 318 leftCol, rightCol := &oc.Columns[left], &other.Columns[right] 319 switch { 320 case leftCol.Descending == rightCol.Descending && leftCol.Group.Intersects(rightCol.Group): 321 // The columns match. 322 left, right = left+1, right+1 323 324 case leftCol.Group.Intersects(other.Optional): 325 // Left column is optional in the right set. 326 left++ 327 328 case rightCol.Group.Intersects(oc.Optional): 329 // Right column is optional in the left set. 330 right++ 331 332 default: 333 return false 334 } 335 } 336 return true 337 } 338 339 // Intersection returns an OrderingChoice that Implies both ordering choices. 340 // Can only be called if Intersects is true. Some examples: 341 // 342 // +1 ∩ <empty> = +1 343 // +1 ∩ +1,+2 = +1,+2 344 // +1,+2 opt(3) ∩ +1,+3 = +1,+3,+2 345 // 346 // In general, OrderingChoice is not expressive enough to represent the 347 // intersection. In such cases, an OrderingChoice representing a subset of the 348 // intersection is returned. For example, 349 // +1 opt(2) ∩ +2 opt(1) 350 // can be either +1,+2 or +2,+1; only one of these is returned. Note that 351 // the function may not be commutative in this case. In practice, such cases are 352 // unlikely. 353 // 354 // It is guaranteed that if one OrderingChoice Implies the other, it will also 355 // be the Intersection. 356 func (oc *OrderingChoice) Intersection(other *OrderingChoice) OrderingChoice { 357 // We have to handle Any cases separately because an Any ordering choice has 358 // no optional columns (even though semantically it should have all possible 359 // columns as optional). 360 if oc.Any() { 361 return other.Copy() 362 } 363 if other.Any() { 364 return oc.Copy() 365 } 366 367 result := make([]OrderingColumnChoice, 0, len(oc.Columns)+len(other.Columns)) 368 369 left, right := 0, 0 370 for left < len(oc.Columns) && right < len(other.Columns) { 371 leftCol, rightCol := &oc.Columns[left], &other.Columns[right] 372 373 switch { 374 case leftCol.Descending == rightCol.Descending && leftCol.Group.Intersects(rightCol.Group): 375 // The columns match. 376 result = append(result, OrderingColumnChoice{ 377 Group: leftCol.Group.Intersection(rightCol.Group), 378 Descending: leftCol.Descending, 379 }) 380 left, right = left+1, right+1 381 382 case leftCol.Group.Intersects(other.Optional): 383 // Left column is optional in the right set. 384 result = append(result, OrderingColumnChoice{ 385 Group: leftCol.Group.Intersection(other.Optional), 386 Descending: leftCol.Descending, 387 }) 388 left++ 389 390 case rightCol.Group.Intersects(oc.Optional): 391 // Right column is optional in the left set. 392 result = append(result, OrderingColumnChoice{ 393 Group: rightCol.Group.Intersection(oc.Optional), 394 Descending: rightCol.Descending, 395 }) 396 right++ 397 398 default: 399 panic(errors.AssertionFailedf("non-intersecting sets")) 400 } 401 } 402 // An ordering matched a prefix of the other. Append the tail of the other 403 // ordering. 404 for ; left < len(oc.Columns); left++ { 405 result = append(result, oc.Columns[left]) 406 } 407 for ; right < len(other.Columns); right++ { 408 result = append(result, other.Columns[right]) 409 } 410 return OrderingChoice{ 411 Optional: oc.Optional.Intersection(other.Optional), 412 Columns: result, 413 } 414 } 415 416 // SubsetOfCols is true if the OrderingChoice only references columns in the 417 // given set. 418 func (oc *OrderingChoice) SubsetOfCols(cs opt.ColSet) bool { 419 if !oc.Optional.SubsetOf(cs) { 420 return false 421 } 422 for i := range oc.Columns { 423 if !oc.Columns[i].Group.SubsetOf(cs) { 424 return false 425 } 426 } 427 return true 428 } 429 430 // CanProjectCols is true if at least one column in each ordering column group is 431 // part of the given column set. For example, if the OrderingChoice is: 432 // 433 // +1,-(2|3) opt(4,5) 434 // 435 // then CanProjectCols will behave as follows for these input sets: 436 // 437 // (1,2) => true 438 // (1,3) => true 439 // (1,2,4) => true 440 // (1) => false 441 // (3,4) => false 442 // 443 func (oc *OrderingChoice) CanProjectCols(cs opt.ColSet) bool { 444 for i := range oc.Columns { 445 if !oc.Columns[i].Group.Intersects(cs) { 446 return false 447 } 448 } 449 return true 450 } 451 452 // MatchesAt returns true if the ordering column at the given index in this 453 // instance matches the given column. The column matches if its id is part of 454 // the equivalence group and if it has the same direction. 455 func (oc *OrderingChoice) MatchesAt(index int, col opt.OrderingColumn) bool { 456 if oc.Optional.Contains(col.ID()) { 457 return true 458 } 459 choice := &oc.Columns[index] 460 if choice.Descending != col.Descending() { 461 return false 462 } 463 if !choice.Group.Contains(col.ID()) { 464 return false 465 } 466 return true 467 } 468 469 // AppendCol adds a new column to the end of the sequence of ordering columns 470 // maintained by this instance. The new column has the given ID and direction as 471 // the only ordering choice. 472 func (oc *OrderingChoice) AppendCol(id opt.ColumnID, descending bool) { 473 ordCol := OrderingColumnChoice{Descending: descending} 474 ordCol.Group.Add(id) 475 oc.Optional.Remove(id) 476 oc.Columns = append(oc.Columns, ordCol) 477 } 478 479 // Copy returns a complete copy of this instance, with a private version of the 480 // ordering column array. 481 func (oc *OrderingChoice) Copy() OrderingChoice { 482 var other OrderingChoice 483 other.Optional = oc.Optional 484 other.Columns = make([]OrderingColumnChoice, len(oc.Columns)) 485 copy(other.Columns, oc.Columns) 486 return other 487 } 488 489 // CanSimplify returns true if a call to Simplify would result in any changes to 490 // the OrderingChoice. Changes include additional constant columns, removed 491 // groups, and additional equivalent columns. This is used to quickly check 492 // whether Simplify needs to be called without requiring allocations in the 493 // common case. This logic should be changed in concert with the Simplify logic. 494 func (oc *OrderingChoice) CanSimplify(fdset *props.FuncDepSet) bool { 495 if oc.Any() { 496 // Any ordering allowed, so can't simplify further. 497 return false 498 } 499 500 // Check whether optional columns can be added by the FD set. 501 optional := fdset.ComputeClosure(oc.Optional) 502 if !optional.Equals(oc.Optional) { 503 return true 504 } 505 506 closure := optional 507 for i := range oc.Columns { 508 group := &oc.Columns[i] 509 510 // If group contains an optional column, then group can be simplified 511 // or removed entirely. 512 if group.Group.Intersects(optional) { 513 return true 514 } 515 516 // If group is functionally determined by previous groups, then it can 517 // be removed entirely. 518 if group.Group.SubsetOf(closure) { 519 return true 520 } 521 522 // Check whether new equivalent columns can be added by the FD set. 523 equiv := fdset.ComputeEquivClosure(group.Group) 524 if !equiv.Equals(group.Group) { 525 return true 526 } 527 528 // Add this group's columns and find closure with new columns. 529 closure.UnionWith(equiv) 530 closure = fdset.ComputeClosure(closure) 531 } 532 533 return false 534 } 535 536 // Simplify uses the given FD set to streamline the orderings allowed by this 537 // instance, and to potentially increase the number of allowed orderings: 538 // 539 // 1. Constant columns add additional optional column choices. 540 // 541 // 2. Equivalent columns allow additional choices within an ordering column 542 // group. 543 // 544 // 3. If the columns in a group are functionally determined by columns from 545 // previous groups, the group can be dropped. This technique is described 546 // in the "Reduce Order" section of this paper: 547 // 548 // Simmen, David & Shekita, Eugene & Malkemus, Timothy. (1996). 549 // Fundamental Techniques for Order Optimization. 550 // Sigmod Record. Volume 25 Issue 2, June 1996. Pages 57-67. 551 // https://cs.uwaterloo.ca/~gweddell/cs798/p57-simmen.pdf 552 // 553 // This logic should be changed in concert with the CanSimplify logic. 554 func (oc *OrderingChoice) Simplify(fdset *props.FuncDepSet) { 555 oc.Optional = fdset.ComputeClosure(oc.Optional) 556 557 closure := oc.Optional 558 n := 0 559 for i := range oc.Columns { 560 group := &oc.Columns[i] 561 562 // Constant columns from the FD set become optional ordering columns and 563 // so can be removed. 564 if group.Group.Intersects(oc.Optional) { 565 if group.Group.SubsetOf(oc.Optional) { 566 continue 567 } 568 group.Group = group.Group.Difference(oc.Optional) 569 } 570 571 // If this group is functionally determined from previous groups, then 572 // discard it. 573 if group.Group.SubsetOf(closure) { 574 continue 575 } 576 577 // Expand group with equivalent columns from FD set. 578 group.Group = fdset.ComputeEquivClosure(group.Group) 579 580 // Add this group's columns and find closure with the new columns. 581 closure = closure.Union(group.Group) 582 closure = fdset.ComputeClosure(closure) 583 584 if n != i { 585 oc.Columns[n] = oc.Columns[i] 586 } 587 n++ 588 } 589 oc.Columns = oc.Columns[:n] 590 591 if len(oc.Columns) == 0 { 592 // Normalize Any case by dropping any optional columns. 593 oc.Optional = opt.ColSet{} 594 } 595 } 596 597 // Truncate removes all ordering columns beyond the given index. For example, 598 // +1,+(2|3),-4 opt(5,6) would be truncated to: 599 // 600 // prefix=0 => opt(5,6) 601 // prefix=1 => +1 opt(5,6) 602 // prefix=2 => +1,+(2|3) opt(5,6) 603 // prefix=3+ => +1,+(2|3),-4 opt(5,6) 604 // 605 func (oc *OrderingChoice) Truncate(prefix int) { 606 if prefix < len(oc.Columns) { 607 oc.Columns = oc.Columns[:prefix] 608 if len(oc.Columns) == 0 { 609 // Normalize Any case by dropping any optional columns. 610 oc.Optional = opt.ColSet{} 611 } 612 } 613 } 614 615 // ProjectCols removes any references to columns that are not in the given 616 // set. This method can only be used when the OrderingChoice can be expressed 617 // with the given columns; i.e. all groups have at least one column in the set. 618 func (oc *OrderingChoice) ProjectCols(cols opt.ColSet) { 619 if !oc.Optional.SubsetOf(cols) { 620 oc.Optional = oc.Optional.Intersection(cols) 621 } 622 for i := range oc.Columns { 623 if !oc.Columns[i].Group.SubsetOf(cols) { 624 oc.Columns[i].Group = oc.Columns[i].Group.Intersection(cols) 625 if oc.Columns[i].Group.Empty() { 626 panic(errors.AssertionFailedf("no columns left from group")) 627 } 628 } 629 } 630 } 631 632 // PrefixIntersection computes an OrderingChoice which: 633 // - implies <oc> (this instance), and 634 // - implies a "segmented ordering", which is any ordering which starts with a 635 // permutation of all columns in <prefix> followed by the <suffix> ordering. 636 // 637 // Note that <prefix> and <suffix> cannot have any columns in common. 638 // 639 // Such an ordering can be computed via the following rules: 640 // 641 // - if <prefix> and <suffix> are empty: return this instance. 642 // 643 // - if <oc> is empty: generate an arbitrary segmented ordering. 644 // 645 // - if the first column of <oc> is either in <prefix> or is the first column 646 // of <suffix> while <prefix> is empty: this column is the first column of 647 // the result; calculate the rest recursively. 648 // 649 func (oc OrderingChoice) PrefixIntersection( 650 prefix opt.ColSet, suffix []OrderingColumnChoice, 651 ) (_ OrderingChoice, ok bool) { 652 var result OrderingChoice 653 oc = oc.Copy() 654 655 prefix = prefix.Copy() 656 657 for { 658 switch { 659 case prefix.Empty() && len(suffix) == 0: 660 // Any ordering is allowed by <prefix>+<suffix>, so use <oc> directly. 661 result.Columns = append(result.Columns, oc.Columns...) 662 return result, true 663 case len(oc.Columns) == 0: 664 // Any ordering is allowed by <oc>, so pick an arbitrary ordering of the 665 // columns in <prefix> then append suffix. 666 // TODO(justin): investigate picking an order more intelligently here. 667 for col, ok := prefix.Next(0); ok; col, ok = prefix.Next(col + 1) { 668 result.AppendCol(col, false /* descending */) 669 } 670 671 result.Columns = append(result.Columns, suffix...) 672 return result, true 673 case prefix.Empty() && len(oc.Columns) > 0 && len(suffix) > 0 && 674 oc.Columns[0].Group.Intersects(suffix[0].Group) && 675 oc.Columns[0].Descending == suffix[0].Descending: 676 // <prefix> is empty, and <suffix> and <oc> agree on the first column, so 677 // emit that column, remove it from both, and loop. 678 newCol := oc.Columns[0] 679 newCol.Group = oc.Columns[0].Group.Intersection(suffix[0].Group) 680 result.Columns = append(result.Columns, newCol) 681 682 oc.Columns = oc.Columns[1:] 683 suffix = suffix[1:] 684 case len(oc.Columns) > 0 && prefix.Intersects(oc.Columns[0].Group): 685 // <prefix> contains the first column in <oc>, so emit it and remove it 686 // from both. 687 result.Columns = append(result.Columns, oc.Columns[0]) 688 689 prefix.DifferenceWith(oc.Columns[0].Group) 690 oc.Columns = oc.Columns[1:] 691 default: 692 // If no rule applied, fail. 693 return OrderingChoice{}, false 694 } 695 } 696 } 697 698 // Equals returns true if the set of orderings matched by this instance is the 699 // same as the set matched by the given instance. 700 func (oc *OrderingChoice) Equals(rhs *OrderingChoice) bool { 701 if len(oc.Columns) != len(rhs.Columns) { 702 return false 703 } 704 if !oc.Optional.Equals(rhs.Optional) { 705 return false 706 } 707 708 for i := range oc.Columns { 709 left := &oc.Columns[i] 710 y := &rhs.Columns[i] 711 712 if left.Descending != y.Descending { 713 return false 714 } 715 if !left.Group.Equals(y.Group) { 716 return false 717 } 718 } 719 return true 720 } 721 722 func (oc OrderingChoice) String() string { 723 var buf bytes.Buffer 724 oc.Format(&buf) 725 return buf.String() 726 } 727 728 // Format writes the OrderingChoice to the given buffer in a human-readable 729 // string representation that can also be parsed by ParseOrderingChoice: 730 // 731 // +1 732 // +1,-2 733 // +(1|2) 734 // +(1|2),+3 735 // -(3|4),+5 opt(1,2) 736 // 737 func (oc OrderingChoice) Format(buf *bytes.Buffer) { 738 for g := range oc.Columns { 739 group := &oc.Columns[g] 740 count := group.Group.Len() 741 742 if group.Descending { 743 buf.WriteByte('-') 744 } else { 745 buf.WriteByte('+') 746 } 747 748 // Write equivalence group. 749 if count > 1 { 750 buf.WriteByte('(') 751 } 752 first := true 753 for i, ok := group.Group.Next(0); ok; i, ok = group.Group.Next(i + 1) { 754 if !first { 755 buf.WriteByte('|') 756 } else { 757 first = false 758 } 759 fmt.Fprintf(buf, "%d", i) 760 } 761 if count > 1 { 762 buf.WriteByte(')') 763 } 764 765 if g+1 != len(oc.Columns) { 766 buf.WriteByte(',') 767 } 768 } 769 770 // Write set of optional columns. 771 if !oc.Optional.Empty() { 772 if len(oc.Columns) != 0 { 773 buf.WriteByte(' ') 774 } 775 fmt.Fprintf(buf, "opt%s", oc.Optional) 776 } 777 } 778 779 // AnyID returns the ID of an arbitrary member of the group of equivalent 780 // columns. 781 func (oc *OrderingColumnChoice) AnyID() opt.ColumnID { 782 id, ok := oc.Group.Next(0) 783 if !ok { 784 panic(errors.AssertionFailedf("column choice group should have at least one column id")) 785 } 786 return id 787 }