From b6e61de03e35cc0ebd2b0bad3b36ae0478dd5ecb Mon Sep 17 00:00:00 2001 From: winkyao Date: Thu, 26 Jul 2018 15:08:34 +0800 Subject: [PATCH 1/2] fix a bug that nullable unique index can not find null values using index. --- distsql/request_builder.go | 13 +++++++++++++ executor/distsql_test.go | 25 +++++++++++++++++++++++++ util/ranger/ranger.go | 1 + util/ranger/types.go | 4 ++++ 4 files changed, 43 insertions(+) diff --git a/distsql/request_builder.go b/distsql/request_builder.go index df1bb065b37a9..caaaad83bcc35 100644 --- a/distsql/request_builder.go +++ b/distsql/request_builder.go @@ -314,8 +314,21 @@ func encodeIndexKey(sc *stmtctx.StatementContext, ran *ranger.Range) ([]byte, [] if err != nil { return nil, nil, errors.Trace(err) } + if !ran.HighExclude { high = []byte(kv.Key(high).PrefixNext()) } + + var hasNull bool + for _, highVal := range ran.HighVal { + if highVal.IsNull() { + hasNull = true + } + } + + if hasNull { + // append 0 to make unique-key range [null, null] to be a scan rather than point-get. + high = []byte(kv.Key(high).Next()) + } return low, high, nil } diff --git a/executor/distsql_test.go b/executor/distsql_test.go index b08d58e6552a2..070c10233e1be 100644 --- a/executor/distsql_test.go +++ b/executor/distsql_test.go @@ -182,3 +182,28 @@ func (s *testSuite) TestCorColToRanges(c *C) { // Test IndexLookUpReader. tk.MustQuery("select t.c in (select count(*) from t s use index(idx), t t1 where s.b = t.a and s.c = t1.a) from t").Check(testkit.Rows("1", "0", "0", "0", "0", "0", "0", "0", "0")) } + +func (s *testSuite) TestUniqueKeyNullValueSelect(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + // test null in unique-key + tk.MustExec("create table t (id int default null, c varchar(20), unique id (id));") + tk.MustExec("insert t (c) values ('a'), ('b'), ('c');") + res := tk.MustQuery("select * from t where id is null;") + res.Check(testkit.Rows(" a", " b", " c")) + + // test null in mul unique-key + tk.MustExec("drop table t") + tk.MustExec("create table t (id int default null, b int default 1, c varchar(20), unique id_c(id, b));") + tk.MustExec("insert t (c) values ('a'), ('b'), ('c');") + res = tk.MustQuery("select * from t where id is null and b = 1;") + res.Check(testkit.Rows(" 1 a", " 1 b", " 1 c")) + + tk.MustExec("drop table t") + // test null in non-unique-key + tk.MustExec("create table t (id int default null, c varchar(20), key id (id));") + tk.MustExec("insert t (c) values ('a'), ('b'), ('c');") + res = tk.MustQuery("select * from t where id is null;") + res.Check(testkit.Rows(" a", " b", " c")) +} diff --git a/util/ranger/ranger.go b/util/ranger/ranger.go index 39cd1e36c6ca3..81344f3f8d10e 100644 --- a/util/ranger/ranger.go +++ b/util/ranger/ranger.go @@ -70,6 +70,7 @@ func points2Ranges(sc *stmtctx.StatementContext, rangePoints []point, tp *types. if mysql.HasNotNullFlag(tp.Flag) && endPoint.value.Kind() == types.KindNull { continue } + ran := &Range{ LowVal: []types.Datum{startPoint.value}, LowExclude: startPoint.excl, diff --git a/util/ranger/types.go b/util/ranger/types.go index 33edfc0a5ccfc..9fe40c6fbc565 100644 --- a/util/ranger/types.go +++ b/util/ranger/types.go @@ -67,6 +67,10 @@ func (ran *Range) IsPoint(sc *stmtctx.StatementContext) bool { if cmp != 0 { return false } + + if a.IsNull() && b.IsNull() { + return false + } } return !ran.LowExclude && !ran.HighExclude } From 592594ff0dde625abe27056d6889402028a46691 Mon Sep 17 00:00:00 2001 From: winkyao Date: Thu, 26 Jul 2018 22:23:09 +0800 Subject: [PATCH 2/2] address comment --- distsql/request_builder.go | 3 ++- util/ranger/types.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/distsql/request_builder.go b/distsql/request_builder.go index caaaad83bcc35..5e569a80d7bb3 100644 --- a/distsql/request_builder.go +++ b/distsql/request_builder.go @@ -323,11 +323,12 @@ func encodeIndexKey(sc *stmtctx.StatementContext, ran *ranger.Range) ([]byte, [] for _, highVal := range ran.HighVal { if highVal.IsNull() { hasNull = true + break } } if hasNull { - // append 0 to make unique-key range [null, null] to be a scan rather than point-get. + // Append 0 to make unique-key range [null, null] to be a scan rather than point-get. high = []byte(kv.Key(high).Next()) } return low, high, nil diff --git a/util/ranger/types.go b/util/ranger/types.go index 9fe40c6fbc565..8bf178d2a78fb 100644 --- a/util/ranger/types.go +++ b/util/ranger/types.go @@ -68,7 +68,7 @@ func (ran *Range) IsPoint(sc *stmtctx.StatementContext) bool { return false } - if a.IsNull() && b.IsNull() { + if a.IsNull() { return false } }