Permalink
Switch branches/tags
v2.2.0-alpha.00000000 v2.1.0-beta.20181015 v2.1.0-beta.20181008 v2.1.0-beta.20181001 v2.1.0-beta.20180924 v2.1.0-beta.20180917 v2.1.0-beta.20180910 v2.1.0-beta.20180904 v2.1.0-beta.20180827 v2.1.0-alpha.20180730 v2.1.0-alpha.20180702 v2.1.0-alpha.20180604 v2.1.0-alpha.20180507 v2.1.0-alpha.20180416 v2.1.0-alpha.00000000 v2.0.6 v2.0.6-rc.1 v2.0.5 v2.0.4 v2.0.3 v2.0.2 v2.0.1 v2.0.0 v2.0-rc.1 v2.0-beta.20180326 v2.0-beta.20180319 v2.0-beta.20180312 v2.0-beta.20180305 v2.0-alpha.20180212 v2.0-alpha.20180129 v2.0-alpha.20180122 v2.0-alpha.20180116 v2.0-alpha.20171218 v2.0-alpha.20171218-plus-left-join-fix v1.2-alpha.20171211 v1.2-alpha.20171204 v1.2-alpha.20171113 v1.2-alpha.20171026 v1.2-alpha.20170901 v1.1.9 v1.1.9-rc.1 v1.1.8 v1.1.7 v1.1.6 v1.1.5 v1.1.4 v1.1.3 v1.1.2 v1.1.1 v1.1.0 v1.1.0-rc.1 v1.1-beta.20170928 v1.1-beta.20170921 v1.1-beta.20170907 v1.1-alpha.20170817 v1.1-alpha.20170810 v1.1-alpha.20170803 v1.1-alpha.20170720 v1.1-alpha.20170713 v1.1-alpha.20170629 v1.1-alpha.20170622 v1.1-alpha.20170608 v1.1-alpha.20170601 v1.0.7 v1.0.6 v1.0.5 v1.0.4 v1.0.3 v1.0.2 v1.0.1 v1.0 v1.0-rc.3 v1.0-rc.2 v1.0-rc.1 v0.1-alpha beta-20170420 beta-20170413 beta-20170406 beta-20170330 beta-20170323 beta-20170309 beta-20170223 beta-20170216 beta-20170209 beta-20170126 beta-20170112 beta-20170105 beta-20161215 beta-20161208 beta-20161201 beta-20161110 beta-20161103 beta-20161027 beta-20161013 beta-20161006 beta-20160929 beta-20160915 beta-20160908 beta-20160829 beta-20160728
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
334 lines (283 sloc) 9.01 KB
// Copyright 2018 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package constraint
import (
"testing"
"github.com/cockroachdb/cockroach/pkg/sql/opt"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/util"
)
func TestConstraintSetIntersect(t *testing.T) {
kc1 := testKeyContext(1)
kc2 := testKeyContext(2)
kc12 := testKeyContext(1, 2)
evalCtx := kc1.EvalCtx
test := func(cs *Set, expected string) {
t.Helper()
if cs.String() != expected {
t.Errorf("\nexpected:\n%v\nactual:\n%v", expected, cs.String())
}
}
data := newSpanTestData()
// Simple AND case.
// @1 > 20
var c Constraint
c.InitSingleSpan(kc1, &data.spGt20)
gt20 := SingleConstraint(&c)
test(gt20, "/1: (/20 - ]")
// @1 <= 40
le40 := SingleSpanConstraint(kc1, &data.spLe40)
test(le40, "/1: [ - /40]")
// @1 > 20 AND @1 <= 40
range2040 := gt20.Intersect(evalCtx, le40)
test(range2040, "/1: (/20 - /40]")
range2040 = le40.Intersect(evalCtx, gt20)
test(range2040, "/1: (/20 - /40]")
// Include constraint on multiple columns.
// (@1, @2) >= (10, 15)
gt1015 := SingleSpanConstraint(kc12, &data.spGe1015)
test(gt1015, "/1/2: [/10/15 - ]")
// (@1, @2) >= (10, 15) AND @1 <= 40
multi2 := le40.Intersect(evalCtx, gt1015)
test(multi2, ""+
"/1: [ - /40]; "+
"/1/2: [/10/15 - ]")
multi2 = gt1015.Intersect(evalCtx, le40)
test(multi2, ""+
"/1: [ - /40]; "+
"/1/2: [/10/15 - ]")
// (@1, @2) >= (10, 15) AND @1 <= 40 AND @2 < 80
lt80 := SingleSpanConstraint(kc2, &data.spLt80)
multi3 := lt80.Intersect(evalCtx, multi2)
test(multi3, ""+
"/1: [ - /40]; "+
"/1/2: [/10/15 - ]; "+
"/2: [ - /80)")
multi3 = multi2.Intersect(evalCtx, lt80)
test(multi3, ""+
"/1: [ - /40]; "+
"/1/2: [/10/15 - ]; "+
"/2: [ - /80)")
// Mismatched number of constraints in each set.
eq10 := SingleSpanConstraint(kc1, &data.spEq10)
mismatched := eq10.Intersect(evalCtx, multi3)
test(mismatched, ""+
"/1: [/10 - /10]; "+
"/1/2: [/10/15 - ]; "+
"/2: [ - /80)")
mismatched = multi3.Intersect(evalCtx, eq10)
test(mismatched, ""+
"/1: [/10 - /10]; "+
"/1/2: [/10/15 - ]; "+
"/2: [ - /80)")
// Multiple intersecting constraints on different columns.
diffCols := eq10.Intersect(evalCtx, SingleSpanConstraint(kc2, &data.spGt20))
res := diffCols.Intersect(evalCtx, multi3)
test(res, ""+
"/1: [/10 - /10]; "+
"/1/2: [/10/15 - ]; "+
"/2: (/20 - /80)")
res = multi3.Intersect(evalCtx, diffCols)
test(res, ""+
"/1: [/10 - /10]; "+
"/1/2: [/10/15 - ]; "+
"/2: (/20 - /80)")
// Intersection results in Contradiction.
res = eq10.Intersect(evalCtx, gt20)
test(res, "contradiction")
res = gt20.Intersect(evalCtx, eq10)
test(res, "contradiction")
// Intersect with Unconstrained (identity op).
res = range2040.Intersect(evalCtx, Unconstrained)
test(res, "/1: (/20 - /40]")
res = Unconstrained.Intersect(evalCtx, range2040)
test(res, "/1: (/20 - /40]")
// Intersect with Contradiction (always contradiction).
res = eq10.Intersect(evalCtx, Contradiction)
test(res, "contradiction")
res = Contradiction.Intersect(evalCtx, eq10)
test(res, "contradiction")
}
func TestConstraintSetUnion(t *testing.T) {
kc1 := testKeyContext(1)
kc2 := testKeyContext(2)
kc12 := testKeyContext(1, 2)
evalCtx := kc1.EvalCtx
data := newSpanTestData()
test := func(cs *Set, expected string) {
t.Helper()
if cs.String() != expected {
t.Errorf("\nexpected:\n%vactual:\n%v", expected, cs.String())
}
}
// Simple OR case.
// @1 > 20
gt20 := SingleSpanConstraint(kc1, &data.spGt20)
test(gt20, "/1: (/20 - ]")
// @1 = 10
eq10 := SingleSpanConstraint(kc1, &data.spEq10)
test(eq10, "/1: [/10 - /10]")
// @1 > 20 OR @1 = 10
gt20eq10 := gt20.Union(evalCtx, eq10)
test(gt20eq10, "/1: [/10 - /10] (/20 - ]")
gt20eq10 = eq10.Union(evalCtx, gt20)
test(gt20eq10, "/1: [/10 - /10] (/20 - ]")
// Combine constraints that result in full span and unconstrained result.
// @1 > 20 OR @1 = 10 OR @1 <= 40
le40 := SingleSpanConstraint(kc1, &data.spLe40)
res := gt20eq10.Union(evalCtx, le40)
test(res, "unconstrained")
res = le40.Union(evalCtx, gt20eq10)
test(res, "unconstrained")
// Include constraint on multiple columns and union with itself.
// (@1, @2) >= (10, 15)
gt1015 := SingleSpanConstraint(kc12, &data.spGe1015)
res = gt1015.Union(evalCtx, gt1015)
test(res, "/1/2: [/10/15 - ]")
// Union incompatible constraints (both are discarded).
// (@1, @2) >= (10, 15) OR @2 < 80
lt80 := SingleSpanConstraint(kc2, &data.spLt80)
res = gt1015.Union(evalCtx, lt80)
test(res, "unconstrained")
res = lt80.Union(evalCtx, gt1015)
test(res, "unconstrained")
// Union two sets with multiple and differing numbers of constraints.
// ((@1, @2) >= (10, 15) AND @2 < 80 AND @1 > 20) OR (@1 = 10 AND @2 = 80)
multi3 := gt1015.Intersect(evalCtx, lt80)
multi3 = multi3.Intersect(evalCtx, gt20)
eq80 := SingleSpanConstraint(kc2, &data.spEq80)
multi2 := eq10.Intersect(evalCtx, eq80)
res = multi3.Union(evalCtx, multi2)
test(res, ""+
"/1: [/10 - /10] (/20 - ]; "+
"/2: [ - /80]")
res = multi2.Union(evalCtx, multi3)
test(res, ""+
"/1: [/10 - /10] (/20 - ]; "+
"/2: [ - /80]")
// Do same as previous, but in different order so that discarded constraint
// is at end of list rather than beginning.
// (@1 > 20 AND @2 < 80 AND (@1, @2) >= (10, 15)) OR (@1 = 10 AND @2 = 80)
multi3 = gt20.Intersect(evalCtx, lt80)
multi3 = multi3.Intersect(evalCtx, gt1015)
res = multi3.Union(evalCtx, multi2)
test(res, ""+
"/1: [/10 - /10] (/20 - ]; "+
"/2: [ - /80]")
res = multi2.Union(evalCtx, multi3)
test(res, ""+
"/1: [/10 - /10] (/20 - ]; "+
"/2: [ - /80]")
// Union with Unconstrained (always unconstrained).
res = gt20.Union(evalCtx, Unconstrained)
test(res, "unconstrained")
res = Unconstrained.Union(evalCtx, gt20)
test(res, "unconstrained")
// Union with Contradiction (identity op).
res = eq10.Union(evalCtx, Contradiction)
test(res, "/1: [/10 - /10]")
res = Contradiction.Union(evalCtx, eq10)
test(res, "/1: [/10 - /10]")
}
func TestExtractConstCols(t *testing.T) {
type testCase struct {
constraints []string
expected opt.ColSet
}
cols := util.MakeFastIntSet
cases := []testCase{
{[]string{`/1: [/10 - /10]`}, cols(1)},
{[]string{`/-1: [/10 - /10]`}, cols(1)},
{[]string{`/1: [/10 - /11]`}, cols()},
{[]string{`/1: [/10 - ]`}, cols()},
{[]string{`/1/2: [/10/2 - /10/4]`}, cols(1)},
{[]string{`/1/-2: [/10/4 - /10/2]`}, cols(1)},
{[]string{`/1/2: [/10/2 - /10/2]`}, cols(1, 2)},
{[]string{`/1/2: [/10/2 - /12/2]`}, cols()},
{[]string{`/1/2: [/9/2 - /9/2] [/10/2 - /12/2]`}, cols()},
{[]string{`/1: [/10 - /10] [/12 - /12]`}, cols()},
{
[]string{
`/1: [/10 - /10]`,
`/2: [/8 - /8]`,
`/-3: [/13 - /7]`,
},
cols(1, 2),
},
{
[]string{
`/1/2: [/10/4 - /10/5] [/12/4 - /12/5]`,
`/2: [/4 - /4]`,
},
cols(2),
},
{[]string{`/1: [/10 - /11)`}, cols()},
// TODO(justin): column 1 here is constant but we don't infer it as such.
{
[]string{
`/2/1: [/900/4 - /900/4] [/1000/4 - /1000/4] [/1100/4 - /1100/4] [/1400/4 - /1400/4] [/1500/4 - /1500/4]`,
},
cols(),
},
{
[]string{
`/1: [/2 - /3]`,
`/2/1: [/10/3 - /11/1]`,
},
cols(),
},
}
evalCtx := tree.NewTestingEvalContext(nil)
for _, tc := range cases {
cs := Unconstrained
for _, constraint := range tc.constraints {
constraint := ParseConstraint(evalCtx, constraint)
cs = cs.Intersect(evalCtx, SingleConstraint(&constraint))
}
cols := cs.ExtractConstCols(evalCtx)
if !tc.expected.Equals(cols) {
t.Errorf("expected constant columns from %s to be %s, was %s", cs, tc.expected, cols)
}
}
}
type spanTestData struct {
spEq10 Span // [/10 - /10]
spGt20 Span // (/20 - ]
spLe40 Span // [ - /40]
spLt80 Span // [ - /80)
spEq80 Span // [/80 - /80]
spGe1015 Span // [/10/15 - ]
}
func newSpanTestData() *spanTestData {
data := &spanTestData{}
key10 := MakeKey(tree.NewDInt(10))
key15 := MakeKey(tree.NewDInt(15))
key20 := MakeKey(tree.NewDInt(20))
key40 := MakeKey(tree.NewDInt(40))
key80 := MakeKey(tree.NewDInt(80))
// [/10 - /10]
data.spEq10.Init(key10, IncludeBoundary, key10, IncludeBoundary)
// (/20 - ]
data.spGt20.Init(key20, ExcludeBoundary, EmptyKey, IncludeBoundary)
// [ - /40]
data.spLe40.Init(EmptyKey, IncludeBoundary, key40, IncludeBoundary)
// [ - /80)
data.spLt80.Init(EmptyKey, IncludeBoundary, key80, ExcludeBoundary)
// [/80 - /80]
data.spEq80.Init(key80, IncludeBoundary, key80, IncludeBoundary)
// [/10/15 - ]
key1015 := key10.Concat(key15)
data.spGe1015.Init(key1015, IncludeBoundary, EmptyKey, IncludeBoundary)
return data
}