Exemple #1
0
func TestMakeSpans(t *testing.T) {
	defer leaktest.AfterTest(t)()

	testData := []struct {
		expr         string
		columns      []string
		expectedAsc  string
		expectedDesc string
	}{
		{`a = 1`, []string{"a"}, `/1-/2`, `/1-/0`},
		{`a != 1`, []string{"a"}, `/#-`, `-/#`},
		{`a > 1`, []string{"a"}, `/2-`, `-/1`},
		{`a >= 1`, []string{"a"}, `/1-`, `-/0`},
		{`a < 1`, []string{"a"}, `/#-/1`, `/0-/#`},
		{`a <= 1`, []string{"a"}, `/#-/2`, `/1-/#`},
		{`a IS NULL`, []string{"a"}, `-/#`, `/NULL-`},
		{`a IS NOT NULL`, []string{"a"}, `/#-`, `-/#`},

		{`a IN (1,2,3)`, []string{"a"}, `/1-/2 /2-/3 /3-/4`, `/3-/2 /2-/1 /1-/0`},
		{`a IN (1,2,3) AND b = 1`, []string{"a", "b"},
			`/1/1-/1/2 /2/1-/2/2 /3/1-/3/2`, `/3/1-/3/0 /2/1-/2/0 /1/1-/1/0`},
		{`a = 1 AND b IN (1,2,3)`, []string{"a", "b"},
			`/1/1-/1/2 /1/2-/1/3 /1/3-/1/4`, `/1/3-/1/2 /1/2-/1/1 /1/1-/1/0`},
		{`a >= 1 AND b IN (1,2,3)`, []string{"a", "b"}, `/1-`, `-/0`},
		{`a <= 1 AND b IN (1,2,3)`, []string{"a", "b"}, `/#-/2`, `/1-/#`},
		{`(a, b) IN ((1, 2), (3, 4))`, []string{"a", "b"},
			`/1/2-/1/3 /3/4-/3/5`, `/3/4-/3/3 /1/2-/1/1`},
		{`(b, a) IN ((1, 2), (3, 4))`, []string{"a", "b"},
			`/2/1-/2/2 /4/3-/4/4`, `/4/3-/4/2 /2/1-/2/0`},
		{`(a, b) IN ((1, 2), (3, 4))`, []string{"b"}, `/2-/3 /4-/5`, `/4-/3 /2-/1`},

		{`a = 1 AND b = 1`, []string{"a", "b"}, `/1/1-/1/2`, `/1/1-/1/0`},
		{`a = 1 AND b != 1`, []string{"a", "b"}, `/1/#-/2`, `/1-/1/#`},
		{`a = 1 AND b > 1`, []string{"a", "b"}, `/1/2-/2`, `/1-/1/1`},
		{`a = 1 AND b >= 1`, []string{"a", "b"}, `/1/1-/2`, `/1-/1/0`},
		{`a = 1 AND b < 1`, []string{"a", "b"}, `/1/#-/1/1`, `/1/0-/1/#`},
		{`a = 1 AND b <= 1`, []string{"a", "b"}, `/1/#-/1/2`, `/1/1-/1/#`},
		{`a = 1 AND b IS NULL`, []string{"a", "b"}, `/1-/1/#`, `/1/NULL-/0`},
		{`a = 1 AND b IS NOT NULL`, []string{"a", "b"}, `/1/#-/2`, `/1-/1/#`},

		{`a != 1 AND b = 1`, []string{"a", "b"}, `/#-`, `-/#`},
		{`a != 1 AND b != 1`, []string{"a", "b"}, `/#-`, `-/#`},
		{`a != 1 AND b > 1`, []string{"a", "b"}, `/#-`, `-/#`},
		{`a != 1 AND b >= 1`, []string{"a", "b"}, `/#-`, `-/#`},
		{`a != 1 AND b < 1`, []string{"a", "b"}, `/#-`, `-/#`},
		{`a != 1 AND b <= 1`, []string{"a", "b"}, `/#-`, `-/#`},
		{`a != 1 AND b IS NULL`, []string{"a", "b"}, `/#-`, `-/#`},
		{`a != 1 AND b IS NOT NULL`, []string{"a", "b"}, `/#-`, `-/#`},

		{`a > 1 AND b = 1`, []string{"a", "b"}, `/2/1-`, `-/2/0`},
		{`a > 1 AND b != 1`, []string{"a", "b"}, `/2/#-`, `-/2/#`},
		{`a > 1 AND b > 1`, []string{"a", "b"}, `/2/2-`, `-/2/1`},
		{`a > 1 AND b >= 1`, []string{"a", "b"}, `/2/1-`, `-/2/0`},
		{`a > 1 AND b < 1`, []string{"a", "b"}, `/2-`, `-/1`},
		{`a > 1 AND b <= 1`, []string{"a", "b"}, `/2-`, `-/1`},
		{`a > 1 AND b IS NULL`, []string{"a", "b"}, `/2-`, `-/1`},
		{`a > 1 AND b IS NOT NULL`, []string{"a", "b"}, `/2/#-`, `-/2/#`},

		{`a >= 1 AND b = 1`, []string{"a", "b"}, `/1/1-`, `-/1/0`},
		{`a >= 1 AND b != 1`, []string{"a", "b"}, `/1/#-`, `-/1/#`},
		{`a >= 1 AND b > 1`, []string{"a", "b"}, `/1/2-`, `-/1/1`},
		{`a >= 1 AND b >= 1`, []string{"a", "b"}, `/1/1-`, `-/1/0`},
		{`a >= 1 AND b < 1`, []string{"a", "b"}, `/1-`, `-/0`},
		{`a >= 1 AND b <= 1`, []string{"a", "b"}, `/1-`, `-/0`},
		{`a >= 1 AND b IS NULL`, []string{"a", "b"}, `/1-`, `-/0`},
		{`a >= 1 AND b IS NOT NULL`, []string{"a", "b"}, `/1/#-`, `-/1/#`},

		{`a < 1 AND b = 1`, []string{"a", "b"}, `/#-/0/2`, `/0/1-/#`},
		{`a < 1 AND b != 1`, []string{"a", "b"}, `/#-/1`, `/0-/#`},
		{`a < 1 AND b > 1`, []string{"a", "b"}, `/#-/1`, `/0-/#`},
		{`a < 1 AND b >= 1`, []string{"a", "b"}, `/#-/1`, `/0-/#`},
		{`a < 1 AND b < 1`, []string{"a", "b"}, `/#-/0/1`, `/0/0-/#`},
		{`a < 1 AND b <= 1`, []string{"a", "b"}, `/#-/0/2`, `/0/1-/#`},
		{`a < 1 AND b IS NULL`, []string{"a", "b"}, `/#-/0/#`, `/0/NULL-/#`},
		{`a < 1 AND b IS NOT NULL`, []string{"a", "b"}, `/#-/1`, `/0-/#`},

		{`a <= 1 AND b = 1`, []string{"a", "b"}, `/#-/1/2`, `/1/1-/#`},
		{`a <= 1 AND b != 1`, []string{"a", "b"}, `/#-/2`, `/1-/#`},
		{`a <= 1 AND b > 1`, []string{"a", "b"}, `/#-/2`, `/1-/#`},
		{`a <= 1 AND b >= 1`, []string{"a", "b"}, `/#-/2`, `/1-/#`},
		{`a <= 1 AND b < 1`, []string{"a", "b"}, `/#-/1/1`, `/1/0-/#`},
		{`a <= 1 AND b <= 1`, []string{"a", "b"}, `/#-/1/2`, `/1/1-/#`},
		{`a <= 1 AND b IS NULL`, []string{"a", "b"}, `/#-/1/#`, `/1/NULL-/#`},
		{`a <= 1 AND b IS NOT NULL`, []string{"a", "b"}, `/#-/2`, `/1-/#`},

		{`a IN (1) AND b = 1`, []string{"a", "b"}, `/1/1-/1/2`, `/1/1-/1/0`},
		{`a IN (1) AND b != 1`, []string{"a", "b"}, `/1/#-/2`, `/1-/1/#`},
		{`a IN (1) AND b > 1`, []string{"a", "b"}, `/1/2-/2`, `/1-/1/1`},
		{`a IN (1) AND b >= 1`, []string{"a", "b"}, `/1/1-/2`, `/1-/1/0`},
		{`a IN (1) AND b < 1`, []string{"a", "b"}, `/1/#-/1/1`, `/1/0-/1/#`},
		{`a IN (1) AND b <= 1`, []string{"a", "b"}, `/1/#-/1/2`, `/1/1-/1/#`},
		{`a IN (1) AND b IS NULL`, []string{"a", "b"}, `/1-/1/#`, `/1/NULL-/0`},
		{`a IN (1) AND b IS NOT NULL`, []string{"a", "b"}, `/1/#-/2`, `/1-/1/#`},

		{`(a, b) = (1, 2)`, []string{"a"}, `/1-/2`, `/1-/0`},
		{`(a, b) = (1, 2)`, []string{"a", "b"}, `/1/2-/1/3`, `/1/2-/1/1`},

		// When encoding an end constraint for a maximal datum, we use
		// bytes.PrefixEnd() to go beyond the normal encodings of that datatype.
		{fmt.Sprintf(`a = %d`, math.MaxInt64), []string{"a"},
			`/9223372036854775807-/<util/encoding/encoding.go: ` +
				`varint 9223372036854775808 overflows int64>`, `/9223372036854775807-/9223372036854775806`},
		{fmt.Sprintf(`a = %d`, math.MinInt64), []string{"a"},
			`/-9223372036854775808-/-9223372036854775807`,
			`/-9223372036854775808-/<util/encoding/encoding.go: varint 9223372036854775808 overflows int64>`},
	}
	for _, d := range testData {
		for _, dir := range []encoding.Direction{encoding.Ascending, encoding.Descending} {
			dirs := make([]encoding.Direction, 0, len(d.columns))
			for range d.columns {
				dirs = append(dirs, dir)
			}
			desc, index := makeTestIndex(t, d.columns, dirs)
			constraints, _ := makeConstraints(t, d.expr, desc, index)
			spans := makeSpans(constraints, desc.ID, index)
			s := prettySpans(spans, 2)
			var expected string
			if dir == encoding.Ascending {
				expected = d.expectedAsc
			} else {
				expected = d.expectedDesc
			}
			s = keys.MassagePrettyPrintedSpanForTest(s, indexToDirs(index))
			if expected != s {
				t.Errorf("[index direction: %d] %s: expected %s, but found %s", dir, d.expr, expected, s)
			}
		}
	}

	type Col struct {
		col string
		dir encoding.Direction
	}

	// Test indexes with mixed-directions (some cols Asc, some cols Desc) and other edge cases.
	testData2 := []struct {
		expr     string
		columns  []Col
		expected string
	}{
		{`a = 1 AND b = 5`,
			[]Col{{"a", encoding.Ascending}, {"b", encoding.Descending}, {"c", encoding.Ascending}},
			`/1/5-/1/4`},
		{`a = 7 AND b IN (1,2,3) AND c = false`,
			[]Col{{"a", encoding.Ascending}, {"b", encoding.Descending}, {"c", encoding.Ascending}},
			`/7/3/0-/7/3/1 /7/2/0-/7/2/1 /7/1/0-/7/1/1`},
		// Test different directions for te columns inside a tuple.
		{`(a,b,j) IN ((1,2,3), (4,5,6))`,
			[]Col{{"a", encoding.Descending}, {"b", encoding.Ascending}, {"j", encoding.Descending}},
			`/4/5/6-/4/5/5 /1/2/3-/1/2/2`},
		{`i = E'\xff'`,
			[]Col{{"i", encoding.Ascending}},
			`/"\xff"-/"\xff\x00"`},
		// Test that limits on bytes work correctly: when encoding a descending limit for bytes,
		// we need to go outside the bytes encoding.
		// "\xaa" is encoded as [bytesDescMarker, ^0xaa, <term escape sequence>]
		{`i = E'\xaa'`,
			[]Col{{"i", encoding.Descending}},
			fmt.Sprintf("raw:%c%c\xff\xfe-%c%c\xff\xff",
				encoding.BytesDescMarker, ^byte(0xaa), encoding.BytesDescMarker, ^byte(0xaa))},
	}
	for _, d := range testData2 {
		cols := make([]string, 0, len(d.columns))
		dirs := make([]encoding.Direction, 0, len(d.columns))
		for _, col := range d.columns {
			cols = append(cols, col.col)
			dirs = append(dirs, col.dir)
		}
		desc, index := makeTestIndex(t, cols, dirs)
		constraints, _ := makeConstraints(t, d.expr, desc, index)
		spans := makeSpans(constraints, desc.ID, index)
		var got string
		raw := false
		if strings.HasPrefix(d.expected, "raw:") {
			raw = true
			span := spans[0]
			d.expected = d.expected[4:]
			// Trim the index prefix from the span.
			got = strings.TrimPrefix(string(span.start), string(MakeIndexKeyPrefix(desc.ID, index.ID))) +
				"-" + strings.TrimPrefix(string(span.end), string(MakeIndexKeyPrefix(desc.ID, index.ID)))
		} else {
			got = keys.MassagePrettyPrintedSpanForTest(prettySpans(spans, 2), indexToDirs(index))
		}
		if d.expected != got {
			if !raw {
				t.Errorf("%s: expected %s, but found %s", d.expr, d.expected, got)
			} else {
				t.Errorf("%s: expected %# x, but found %# x", d.expr, []byte(d.expected), got)
			}
		}
	}
}
func TestMakeSpans(t *testing.T) {
	defer leaktest.AfterTest(t)()

	testData := []struct {
		expr         string
		columns      string
		expectedAsc  string
		expectedDesc string
	}{
		{`a = 1`, `a`, `/1-/2`, `/1-/0`},
		{`a != 1`, `a`, `/#-`, `-/#`},
		{`a > 1`, `a`, `/2-`, `-/1`},
		{`a >= 1`, `a`, `/1-`, `-/0`},
		{`a < 1`, `a`, `/#-/1`, `/0-/#`},
		{`a <= 1`, `a`, `/#-/2`, `/1-/#`},
		{`a IS NULL`, `a`, `-/#`, `/NULL-`},
		{`a IS NOT NULL`, `a`, `/#-`, `-/#`},

		{`a IN (1,2,3)`, `a`, `/1-/4`, `/3-/0`},
		{`a IN (1,3,5)`, `a`, `/1-/2 /3-/4 /5-/6`, `/5-/4 /3-/2 /1-/0`},
		{`a IN (1,2,3) AND b = 1`, `a,b`,
			`/1/1-/1/2 /2/1-/2/2 /3/1-/3/2`, `/3/1-/3/0 /2/1-/2/0 /1/1-/1/0`},
		{`a = 1 AND b IN (1,2,3)`, `a,b`,
			`/1/1-/1/4`, `/1/3-/1/0`},
		{`a = 1 AND b IN (1,3,5)`, `a,b`,
			`/1/1-/1/2 /1/3-/1/4 /1/5-/1/6`, `/1/5-/1/4 /1/3-/1/2 /1/1-/1/0`},
		{`a >= 1 AND b IN (1,2,3)`, `a,b`, `/1-`, `-/0`},
		{`a <= 1 AND b IN (1,2,3)`, `a,b`, `/#-/2`, `/1-/#`},
		{`(a, b) IN ((1, 2), (3, 4))`, `a,b`,
			`/1/2-/1/3 /3/4-/3/5`, `/3/4-/3/3 /1/2-/1/1`},
		{`(b, a) IN ((1, 2), (3, 4))`, `a,b`,
			`/2/1-/2/2 /4/3-/4/4`, `/4/3-/4/2 /2/1-/2/0`},
		{`(a, b) IN ((1, 2), (3, 4))`, `b`, `/2-/3 /4-/5`, `/4-/3 /2-/1`},

		{`a = 1 AND b = 1`, `a,b`, `/1/1-/1/2`, `/1/1-/1/0`},
		{`a = 1 AND b != 1`, `a,b`, `/1/#-/2`, `/1-/1/#`},
		{`a = 1 AND b > 1`, `a,b`, `/1/2-/2`, `/1-/1/1`},
		{`a = 1 AND b >= 1`, `a,b`, `/1/1-/2`, `/1-/1/0`},
		{`a = 1 AND b < 1`, `a,b`, `/1/#-/1/1`, `/1/0-/1/#`},
		{`a = 1 AND b <= 1`, `a,b`, `/1/#-/1/2`, `/1/1-/1/#`},
		{`a = 1 AND b IS NULL`, `a,b`, `/1-/1/#`, `/1/NULL-/0`},
		{`a = 1 AND b IS NOT NULL`, `a,b`, `/1/#-/2`, `/1-/1/#`},

		{`a != 1 AND b = 1`, `a,b`, `/#-`, `-/#`},
		{`a != 1 AND b != 1`, `a,b`, `/#-`, `-/#`},
		{`a != 1 AND b > 1`, `a,b`, `/#-`, `-/#`},
		{`a != 1 AND b >= 1`, `a,b`, `/#-`, `-/#`},
		{`a != 1 AND b < 1`, `a,b`, `/#-`, `-/#`},
		{`a != 1 AND b <= 1`, `a,b`, `/#-`, `-/#`},
		{`a != 1 AND b IS NULL`, `a,b`, `/#-`, `-/#`},
		{`a != 1 AND b IS NOT NULL`, `a,b`, `/#-`, `-/#`},

		{`a > 1 AND b = 1`, `a,b`, `/2/1-`, `-/2/0`},
		{`a > 1 AND b != 1`, `a,b`, `/2/#-`, `-/2/#`},
		{`a > 1 AND b > 1`, `a,b`, `/2/2-`, `-/2/1`},
		{`a > 1 AND b >= 1`, `a,b`, `/2/1-`, `-/2/0`},
		{`a > 1 AND b < 1`, `a,b`, `/2-`, `-/1`},
		{`a > 1 AND b <= 1`, `a,b`, `/2-`, `-/1`},
		{`a > 1 AND b IS NULL`, `a,b`, `/2-`, `-/1`},
		{`a > 1 AND b IS NOT NULL`, `a,b`, `/2/#-`, `-/2/#`},

		{`a >= 1 AND b = 1`, `a,b`, `/1/1-`, `-/1/0`},
		{`a >= 1 AND b != 1`, `a,b`, `/1/#-`, `-/1/#`},
		{`a >= 1 AND b > 1`, `a,b`, `/1/2-`, `-/1/1`},
		{`a >= 1 AND b >= 1`, `a,b`, `/1/1-`, `-/1/0`},
		{`a >= 1 AND b < 1`, `a,b`, `/1-`, `-/0`},
		{`a >= 1 AND b <= 1`, `a,b`, `/1-`, `-/0`},
		{`a >= 1 AND b IS NULL`, `a,b`, `/1-`, `-/0`},
		{`a >= 1 AND b IS NOT NULL`, `a,b`, `/1/#-`, `-/1/#`},

		{`a < 1 AND b = 1`, `a,b`, `/#-/0/2`, `/0/1-/#`},
		{`a < 1 AND b != 1`, `a,b`, `/#-/1`, `/0-/#`},
		{`a < 1 AND b > 1`, `a,b`, `/#-/1`, `/0-/#`},
		{`a < 1 AND b >= 1`, `a,b`, `/#-/1`, `/0-/#`},
		{`a < 1 AND b < 1`, `a,b`, `/#-/0/1`, `/0/0-/#`},
		{`a < 1 AND b <= 1`, `a,b`, `/#-/0/2`, `/0/1-/#`},
		{`a < 1 AND b IS NULL`, `a,b`, `/#-/0/#`, `/0/NULL-/#`},
		{`a < 1 AND b IS NOT NULL`, `a,b`, `/#-/1`, `/0-/#`},

		{`a <= 1 AND b = 1`, `a,b`, `/#-/1/2`, `/1/1-/#`},
		{`a <= 1 AND b != 1`, `a,b`, `/#-/2`, `/1-/#`},
		{`a <= 1 AND b > 1`, `a,b`, `/#-/2`, `/1-/#`},
		{`a <= 1 AND b >= 1`, `a,b`, `/#-/2`, `/1-/#`},
		{`a <= 1 AND b < 1`, `a,b`, `/#-/1/1`, `/1/0-/#`},
		{`a <= 1 AND b <= 1`, `a,b`, `/#-/1/2`, `/1/1-/#`},
		{`a <= 1 AND b IS NULL`, `a,b`, `/#-/1/#`, `/1/NULL-/#`},
		{`a <= 1 AND b IS NOT NULL`, `a,b`, `/#-/2`, `/1-/#`},

		{`a IN (1) AND b = 1`, `a,b`, `/1/1-/1/2`, `/1/1-/1/0`},
		{`a IN (1) AND b != 1`, `a,b`, `/1/#-/2`, `/1-/1/#`},
		{`a IN (1) AND b > 1`, `a,b`, `/1/2-/2`, `/1-/1/1`},
		{`a IN (1) AND b >= 1`, `a,b`, `/1/1-/2`, `/1-/1/0`},
		{`a IN (1) AND b < 1`, `a,b`, `/1/#-/1/1`, `/1/0-/1/#`},
		{`a IN (1) AND b <= 1`, `a,b`, `/1/#-/1/2`, `/1/1-/1/#`},
		{`a IN (1) AND b IS NULL`, `a,b`, `/1-/1/#`, `/1/NULL-/0`},
		{`a IN (1) AND b IS NOT NULL`, `a,b`, `/1/#-/2`, `/1-/1/#`},

		{`(a, b) = (1, 2)`, `a`, `/1-/2`, `/1-/0`},
		{`(a, b) = (1, 2)`, `a,b`, `/1/2-/1/3`, `/1/2-/1/1`},

		{`a > 1 OR a >= 5`, `a`, `/2-`, `-/1`},
		{`a < 5 OR a >= 1`, `a`, `/#-`, `-/#`},
		{`a < 1 OR a >= 5`, `a`, `/#-/1 /5-`, `-/4 /0-/#`},
		{`a = 1 OR a > 8`, `a`, `/1-/2 /9-`, `-/8 /1-/0`},
		{`a = 8 OR a > 1`, `a`, `/2-`, `-/1`},
		{`a < 1 OR a = 5 OR a > 8`, `a`, `/#-/1 /5-/6 /9-`, `-/8 /5-/4 /0-/#`},
		{`a < 8 OR a = 8 OR a > 8`, `a`, `/#-`, `-/#`},

		{`(a = 1 AND b = 5) OR (a = 3 AND b = 7)`, `a`, `/1-/2 /3-/4`, `/3-/2 /1-/0`},
		{`(a = 1 AND b = 5) OR (a = 3 AND b = 7)`, `b`, `/5-/6 /7-/8`, `/7-/6 /5-/4`},
		{`(a = 1 AND b = 5) OR (a = 3 AND b = 7)`, `a,b`,
			`/1/5-/1/6 /3/7-/3/8`, `/3/7-/3/6 /1/5-/1/4`},

		{`(a = 1 AND b < 5) OR (a = 3 AND b > 7)`, `a`, `/1-/2 /3-/4`, `/3-/2 /1-/0`},
		{`(a = 1 AND b < 5) OR (a = 3 AND b > 7)`, `b`, `/#-/5 /8-`, `-/7 /4-/#`},
		{`(a = 1 AND b < 5) OR (a = 3 AND b > 7)`, `a,b`,
			`/1/#-/1/5 /3/8-/4`, `/3-/3/7 /1/4-/1/#`},

		{`(a = 1 AND b > 5) OR (a = 3 AND b > 7)`, `a`, `/1-/2 /3-/4`, `/3-/2 /1-/0`},
		{`(a = 1 AND b > 5) OR (a = 3 AND b > 7)`, `b`, `/6-`, `-/5`},
		{`(a = 1 AND b > 5) OR (a = 3 AND b > 7)`, `a,b`,
			`/1/6-/2 /3/8-/4`, `/3-/3/7 /1-/1/5`},

		{`(a = 1 AND b > 5) OR (a = 3 AND b < 7)`, `a`, `/1-/2 /3-/4`, `/3-/2 /1-/0`},
		{`(a = 1 AND b > 5) OR (a = 3 AND b < 7)`, `b`, `/#-`, `-/#`},
		{`(a = 1 AND b > 5) OR (a = 3 AND b < 7)`, `a,b`,
			`/1/6-/2 /3/#-/3/7`, `/3/6-/3/# /1-/1/5`},

		{`(a < 1 AND b < 5) OR (a > 3 AND b > 7)`, `a`, `/#-/1 /4-`, `-/3 /0-/#`},
		{`(a < 1 AND b < 5) OR (a > 3 AND b > 7)`, `b`, `/#-/5 /8-`, `-/7 /4-/#`},
		{`(a < 1 AND b < 5) OR (a > 3 AND b > 7)`, `a,b`,
			`/#-/0/5 /4/8-`, `-/4/7 /0/4-/#`},

		{`(a > 3 AND b < 5) OR (a < 1 AND b > 7)`, `a`, `/#-/1 /4-`, `-/3 /0-/#`},
		{`(a > 3 AND b < 5) OR (a < 1 AND b > 7)`, `b`, `/#-/5 /8-`, `-/7 /4-/#`},
		{`(a > 3 AND b < 5) OR (a < 1 AND b > 7)`, `a,b`,
			`/#-/1 /4-`, `-/3 /0-/#`},

		{`(a > 1 AND b < 5) OR (a < 3 AND b > 7)`, `a`, `/#-`, `-/#`},
		{`(a > 1 AND b < 5) OR (a < 3 AND b > 7)`, `b`, `/#-/5 /8-`, `-/7 /4-/#`},
		{`(a > 1 AND b < 5) OR (a < 3 AND b > 7)`, `a,b`, `/#-`, `-/#`},

		{`(a = 5) OR (a, b) IN ((1, 1), (3, 3))`, `a`, `/1-/2 /3-/4 /5-/6`, `/5-/4 /3-/2 /1-/0`},
		{`(a = 5) OR (a, b) IN ((1, 1), (3, 3))`, `b`, `-`, `-`},
		{`(a = 5) OR (a, b) IN ((1, 1), (3, 3))`, `a,b`,
			`/1/1-/1/2 /3/3-/3/4 /5-/6`, `/5-/4 /3/3-/3/2 /1/1-/1/0`},

		// When encoding an end constraint for a maximal datum, we use
		// bytes.PrefixEnd() to go beyond the normal encodings of that datatype.
		{fmt.Sprintf(`a = %d`, math.MaxInt64), `a`,
			`/9223372036854775807-/<varint 9223372036854775808 overflows int64>`,
			`/9223372036854775807-/9223372036854775806`},
		{fmt.Sprintf(`a = %d`, math.MinInt64), `a`,
			`/-9223372036854775808-/-9223372036854775807`,
			`/-9223372036854775808-/<varint 9223372036854775808 overflows int64>`},

		{`(a, b) >= (1, 4)`, `a,b`, `/1/4-`, `-/1/3`},
		{`(a, b) > (1, 4)`, `a,b`, `/1/5-`, `-/1/4`},
		{`(a, b) < (1, 4)`, `a,b`, `/#-/1/4`, `/1/3-/#`},
		{`(a, b) <= (1, 4)`, `a,b`, `/#-/1/5`, `/1/4-/#`},
		{`(a, b) = (1, 4)`, `a,b`, `/1/4-/1/5`, `/1/4-/1/3`},
		{`(a, b) != (1, 4)`, `a,b`, `/#-`, `-/#`},
	}
	for _, d := range testData {
		for _, dir := range []encoding.Direction{encoding.Ascending, encoding.Descending} {
			columns := strings.Split(d.columns, ",")
			dirs := make([]encoding.Direction, 0, len(columns))
			for range columns {
				dirs = append(dirs, dir)
			}
			desc, index := makeTestIndex(t, columns, dirs)
			constraints, _ := makeConstraints(t, d.expr, desc, index)
			spans := makeSpans(constraints, desc.ID, index)
			s := sqlbase.PrettySpans(spans, 2)
			var expected string
			if dir == encoding.Ascending {
				expected = d.expectedAsc
			} else {
				expected = d.expectedDesc
			}
			s = keys.MassagePrettyPrintedSpanForTest(s, indexToDirs(index))
			if expected != s {
				t.Errorf("[index direction: %d] %s: expected %s, but found %s", dir, d.expr, expected, s)
			}
		}
	}

	// Test indexes with mixed-directions (some cols Asc, some cols Desc) and other edge cases.
	testData2 := []struct {
		expr     string
		columns  string
		expected string
	}{
		{`a = 1 AND b = 5`, `a,b-,c`, `/1/5-/1/4`},
		{`a = 7 AND b IN (1,2,3) AND c = false`, `a,b-,c`,
			`/7/3/0-/7/3/1 /7/2/0-/7/2/1 /7/1/0-/7/1/1`},
		// Test different directions for te columns inside a tuple.
		{`(a,b,j) IN ((1,2,3), (4,5,6))`, `a-,b,j-`, `/4/5/6-/4/5/5 /1/2/3-/1/2/2`},
		{`i = E'\xff'`, `i`, `/"\xff"-/"\xff\x00"`},
		// Test that limits on bytes work correctly: when encoding a descending limit for bytes,
		// we need to go outside the bytes encoding.
		// "\xaa" is encoded as [bytesDescMarker, ^0xaa, <term escape sequence>]
		{`i = E'\xaa'`, `i-`,
			fmt.Sprintf("raw:%c%c\xff\xfe-%c%c\xff\xff",
				encoding.BytesDescMarker, ^byte(0xaa), encoding.BytesDescMarker, ^byte(0xaa))},

		// Ensure tuples with differing index directions aren't constrained.
		// TODO(mjibson): fix this, see #6346
		{`(a, b) >= (1, 4)`, `a-,b`, `-`},
		{`(a, b) >= (1, 4)`, `a,b-`, `-`},
	}
	for _, d := range testData2 {
		desc, index := makeTestIndexFromStr(t, d.columns)
		constraints, _ := makeConstraints(t, d.expr, desc, index)
		spans := makeSpans(constraints, desc.ID, index)
		var got string
		raw := false
		if strings.HasPrefix(d.expected, "raw:") {
			raw = true
			span := spans[0]
			d.expected = d.expected[4:]
			// Trim the index prefix from the span.
			prefix := string(sqlbase.MakeIndexKeyPrefix(desc.ID, index.ID))
			got = strings.TrimPrefix(string(span.Start), prefix) + "-" +
				strings.TrimPrefix(string(span.End), prefix)
		} else {
			got = keys.MassagePrettyPrintedSpanForTest(sqlbase.PrettySpans(spans, 2),
				indexToDirs(index))
		}
		if d.expected != got {
			if !raw {
				t.Errorf("%s: expected %s, but found %s", d.expr, d.expected, got)
			} else {
				t.Errorf("%s: expected %# x, but found %# x", d.expr, []byte(d.expected), got)
			}
		}
	}
}