Skip to content

Commit

Permalink
Merge pull request cockroachdb#19853 from richardwu/interleave-join-p…
Browse files Browse the repository at this point in the history
…lanning-extravaganza

sql, distsql: planning for interleave joins between ancestor and descendant
  • Loading branch information
richardwu authored Dec 11, 2017
2 parents e23cfcc + 69165ab commit ca5eaaf
Show file tree
Hide file tree
Showing 19 changed files with 2,710 additions and 191 deletions.
50 changes: 49 additions & 1 deletion pkg/roachpb/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -1245,8 +1245,17 @@ func (s Span) EqualValue(o Span) bool {
return s.Key.Equal(o.Key) && s.EndKey.Equal(o.EndKey)
}

// Overlaps returns whether the two spans overlap.
// Overlaps returns true WLOG for span A and B iff:
// 1. Both spans contain one key (just the start key) and they are equal; or
// 2. The span with only one key is contained inside the other span; or
// 3. The end key of span A is strictly greater than the start key of span B
// and the end key of span B is strictly greater than the start key of span
// A.
func (s Span) Overlaps(o Span) bool {
if !s.Valid() || !o.Valid() {
return false
}

if len(s.EndKey) == 0 && len(o.EndKey) == 0 {
return s.Key.Equal(o.Key)
} else if len(s.EndKey) == 0 {
Expand All @@ -1259,6 +1268,10 @@ func (s Span) Overlaps(o Span) bool {

// Contains returns whether the receiver contains the given span.
func (s Span) Contains(o Span) bool {
if !s.Valid() || !o.Valid() {
return false
}

if len(s.EndKey) == 0 && len(o.EndKey) == 0 {
return s.Key.Equal(o.Key)
} else if len(s.EndKey) == 0 {
Expand Down Expand Up @@ -1293,6 +1306,41 @@ func (s Span) String() string {
return PrettyPrintRange(s.Key, s.EndKey, maxChars)
}

// SplitOnKey returns two spans where the left span has EndKey and right span
// has start Key of the split key, respectively.
// If the split key lies outside the span, the original span is returned on the
// left (and right is an invalid span with empty keys).
func (s Span) SplitOnKey(key Key) (left Span, right Span) {
// Cannot split on or before start key or on or after end key.
if bytes.Compare(key, s.Key) <= 0 || bytes.Compare(key, s.EndKey) >= 0 {
return s, Span{}
}

return Span{Key: s.Key, EndKey: key}, Span{Key: key, EndKey: s.EndKey}
}

// Valid returns whether or not the span is a "valid span".
// A valid span cannot have an empty start and end key and must satisfy either:
// 1. The end key is empty.
// 2. The start key is lexicographically-ordered before the end key.
func (s Span) Valid() bool {
// s.Key can be empty if it is KeyMin.
// Can't have both KeyMin start and end keys.
if len(s.Key) == 0 && len(s.EndKey) == 0 {
return false
}

if len(s.EndKey) == 0 {
return true
}

if bytes.Compare(s.Key, s.EndKey) >= 0 {
return false
}

return true
}

// Spans is a slice of spans.
type Spans []Span

Expand Down
87 changes: 85 additions & 2 deletions pkg/roachpb/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"math/rand"
"reflect"
"sort"
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -707,6 +708,9 @@ func TestSpanOverlaps(t *testing.T) {
sD := Span{Key: []byte("d")}
sAtoC := Span{Key: []byte("a"), EndKey: []byte("c")}
sBtoD := Span{Key: []byte("b"), EndKey: []byte("d")}
// Invalid spans.
sCtoA := Span{Key: []byte("c"), EndKey: []byte("a")}
sDtoB := Span{Key: []byte("d"), EndKey: []byte("b")}

testData := []struct {
s1, s2 Span
Expand All @@ -723,6 +727,11 @@ func TestSpanOverlaps(t *testing.T) {
{sAtoC, sAtoC, true},
{sAtoC, sBtoD, true},
{sBtoD, sAtoC, true},
// Invalid spans.
{sAtoC, sDtoB, false},
{sDtoB, sAtoC, false},
{sBtoD, sCtoA, false},
{sCtoA, sBtoD, false},
}
for i, test := range testData {
if o := test.s1.Overlaps(test.s2); o != test.overlaps {
Expand Down Expand Up @@ -755,8 +764,7 @@ func TestSpanContains(t *testing.T) {
{[]byte("b"), []byte("bb"), false},
{[]byte("0"), []byte("bb"), false},
{[]byte("aa"), []byte("bb"), false},
// TODO(bdarnell): check for invalid ranges in Span.Contains?
//{[]byte("b"), []byte("a"), false},
{[]byte("b"), []byte("a"), false},
}
for i, test := range testData {
if s.Contains(Span{test.start, test.end}) != test.contains {
Expand All @@ -766,6 +774,81 @@ func TestSpanContains(t *testing.T) {
}
}

func TestSpanSplitOnKey(t *testing.T) {
s := Span{Key: []byte("b"), EndKey: []byte("c")}

testData := []struct {
split []byte
left Span
right Span
}{
// Split on start/end key should fail.
{
[]byte("b"),
s,
Span{},
},
{
[]byte("c"),
s,
Span{},
},

// Before start key.
{
[]byte("a"),
s,
Span{},
},
// After end key.
{
[]byte("d"),
s,
Span{},
},

// Simple split.
{
[]byte("bb"),
Span{[]byte("b"), []byte("bb")},
Span{[]byte("bb"), []byte("c")},
},
}
for testIdx, test := range testData {
t.Run(strconv.Itoa(testIdx), func(t *testing.T) {
actualL, actualR := s.SplitOnKey(test.split)
if !test.left.EqualValue(actualL) {
t.Fatalf("expected left span after split to be %v, got %v", test.left, actualL)
}

if !test.right.EqualValue(actualR) {
t.Fatalf("expected right span after split to be %v, got %v", test.right, actualL)
}
})
}
}

func TestSpanValid(t *testing.T) {
testData := []struct {
start, end []byte
valid bool
}{
{[]byte("a"), nil, true},
{[]byte("a"), []byte("b"), true},
{[]byte(""), []byte(""), false},
{[]byte(""), []byte("a"), true},
{[]byte("a"), []byte("a"), false},
{[]byte("b"), []byte("aa"), false},
}
for i, test := range testData {
s := Span{test.start, test.end}
if test.valid != s.Valid() {
t.Errorf("%d: expected span %q-%q to return %t for Valid, instead got %t",
i, test.start, test.end, test.valid, s.Valid())
}
}
}

// TestRSpanContains verifies methods to check whether a key
// or key range is contained within the span.
func TestRSpanContains(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions pkg/roachpb/merge_spans.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func (s sortedSpans) Len() int {

// MergeSpans sorts the incoming spans and merges overlapping spans. Returns
// true iff all of the spans are distinct.
// The input spans are not safe for re-use.
func MergeSpans(spans []Span) ([]Span, bool) {
if len(spans) == 0 {
return spans, true
Expand Down
29 changes: 15 additions & 14 deletions pkg/roachpb/merge_spans_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,25 @@ import (
"testing"
)

func TestMergeSpans(t *testing.T) {
makeSpan := func(s string) Span {
parts := strings.Split(s, "-")
if len(parts) == 2 {
return Span{Key: Key(parts[0]), EndKey: Key(parts[1])}
}
return Span{Key: Key(s)}
func makeSpan(s string) Span {
parts := strings.Split(s, "-")
if len(parts) == 2 {
return Span{Key: Key(parts[0]), EndKey: Key(parts[1])}
}
makeSpans := func(s string) []Span {
var spans []Span
if len(s) > 0 {
for _, p := range strings.Split(s, ",") {
spans = append(spans, makeSpan(p))
}
return Span{Key: Key(s)}
}

func makeSpans(s string) []Span {
var spans []Span
if len(s) > 0 {
for _, p := range strings.Split(s, ",") {
spans = append(spans, makeSpan(p))
}
return spans
}
return spans
}

func TestMergeSpans(t *testing.T) {
testCases := []struct {
spans string
expected string
Expand Down
Loading

0 comments on commit ca5eaaf

Please sign in to comment.