diff --git a/executor/batch_point_get.go b/executor/batch_point_get.go index 122a858e9631d..396ebcc77fbaa 100644 --- a/executor/batch_point_get.go +++ b/executor/batch_point_get.go @@ -150,10 +150,13 @@ func (e *BatchPointGetExec) initialize(ctx context.Context) error { keys := make([]kv.Key, 0, len(e.idxVals)) for _, idxVals := range e.idxVals { physID := getPhysID(e.tblInfo, idxVals[e.partPos].GetInt64()) - idxKey, err1 := encodeIndexKey(e.base(), e.tblInfo, e.idxInfo, idxVals, physID) + idxKey, hasNull, err1 := encodeIndexKey(e.base(), e.tblInfo, e.idxInfo, idxVals, physID) if err1 != nil && !kv.ErrNotExist.Equal(err1) { return err1 } + if hasNull { + continue + } s := hack.String(idxKey) if _, found := dedup[s]; found { continue diff --git a/executor/point_get.go b/executor/point_get.go index a12b2633ee3aa..f55a301ad17fe 100644 --- a/executor/point_get.go +++ b/executor/point_get.go @@ -157,10 +157,14 @@ func (e *PointGetExecutor) Next(ctx context.Context, req *chunk.Chunk) error { tblID = e.tblInfo.ID } if e.idxInfo != nil { - e.idxKey, err = encodeIndexKey(e.base(), e.tblInfo, e.idxInfo, e.idxVals, tblID) + hasNull := false + e.idxKey, hasNull, err = encodeIndexKey(e.base(), e.tblInfo, e.idxInfo, e.idxVals, tblID) if err != nil && !kv.ErrNotExist.Equal(err) { return err } + if hasNull { + return nil + } e.handleVal, err = e.get(ctx, e.idxKey) if err != nil { @@ -299,9 +303,13 @@ func (e *PointGetExecutor) get(ctx context.Context, key kv.Key) ([]byte, error) return e.snapshot.Get(ctx, key) } -func encodeIndexKey(e *baseExecutor, tblInfo *model.TableInfo, idxInfo *model.IndexInfo, idxVals []types.Datum, tID int64) (_ []byte, err error) { +func encodeIndexKey(e *baseExecutor, tblInfo *model.TableInfo, idxInfo *model.IndexInfo, idxVals []types.Datum, tID int64) (_ []byte, hasNull bool, err error) { sc := e.ctx.GetSessionVars().StmtCtx for i := range idxVals { + if idxVals[i].IsNull() { + hasNull = true + continue + } colInfo := tblInfo.Columns[idxInfo.Columns[i].Offset] // table.CastValue will append 0x0 if the string value's length is smaller than the BINARY column's length. // So we don't use CastValue for string value for now. @@ -313,19 +321,19 @@ func encodeIndexKey(e *baseExecutor, tblInfo *model.TableInfo, idxInfo *model.In } else { idxVals[i], err = table.CastValue(e.ctx, idxVals[i], colInfo, true, false) if types.ErrOverflow.Equal(err) { - return nil, kv.ErrNotExist + return nil, false, kv.ErrNotExist } } if err != nil { - return nil, err + return nil, false, err } } encodedIdxVals, err := codec.EncodeKey(sc, nil, idxVals...) if err != nil { - return nil, err + return nil, false, err } - return tablecodec.EncodeIndexSeekKey(tID, idxInfo.ID, encodedIdxVals), nil + return tablecodec.EncodeIndexSeekKey(tID, idxInfo.ID, encodedIdxVals), hasNull, nil } func decodeRowValToChunk(e *baseExecutor, tblInfo *model.TableInfo, handle int64, rowVal []byte, chk *chunk.Chunk, rd *rowcodec.ChunkDecoder) error { diff --git a/executor/point_get_test.go b/executor/point_get_test.go index 24ef8513298f4..8a657b0bc7524 100644 --- a/executor/point_get_test.go +++ b/executor/point_get_test.go @@ -512,6 +512,16 @@ func (s *testPointGetSuite) TestSelectCheckVisibility(c *C) { checkSelectResultError("select * from t", tikv.ErrGCTooEarly) } +func (s *testPointGetSuite) TestNullValues(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t ( id bigint(10) primary key, f varchar(191) default null, unique key `idx_f` (`f`))") + tk.MustExec(`insert into t values (1, "")`) + rs := tk.MustQuery(`select * from t where f in (null)`).Rows() + c.Assert(len(rs), Equals, 0) +} + func (s *testPointGetSuite) TestReturnValues(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test")