From dec9371d8a72dd0826e7839c6ef4b6bdca68b886 Mon Sep 17 00:00:00 2001 From: Feng Liyuan Date: Wed, 1 Apr 2020 21:18:58 +0800 Subject: [PATCH] executor: fix building hash table with TypeNull when join (#15913) --- executor/join_test.go | 16 ++++++++++++++ util/codec/codec.go | 9 ++++++++ util/codec/codec_test.go | 47 ++++++++++++++++++++++------------------ 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/executor/join_test.go b/executor/join_test.go index 08f463dbba27a..8ce5592471402 100644 --- a/executor/join_test.go +++ b/executor/join_test.go @@ -1014,6 +1014,22 @@ func (s *testSuiteJoin1) TestIssue5278(c *C) { tk.MustQuery("select * from t left join tt on t.a=tt.a left join t ttt on t.a=ttt.a").Check(testkit.Rows("1 1 1 1")) } +func (s *testSuiteJoin1) TestIssue15850JoinNullValue(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustQuery("SELECT * FROM (select null) v NATURAL LEFT JOIN (select null) v1;").Check(testkit.Rows("")) + c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(0)) + + tk.MustExec("drop table if exists t0;") + tk.MustExec("drop view if exists v0;") + tk.MustExec("CREATE TABLE t0(c0 TEXT);") + tk.MustExec("CREATE VIEW v0(c0) AS SELECT NULL;") + tk.MustQuery("SELECT /*+ HASH_JOIN(v0) */ * FROM v0 NATURAL LEFT JOIN t0;").Check(testkit.Rows("")) + c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(0)) + tk.MustQuery("SELECT /*+ MERGE_JOIN(v0) */ * FROM v0 NATURAL LEFT JOIN t0;").Check(testkit.Rows("")) + c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(0)) +} + func (s *testSuiteJoin1) TestIndexLookupJoin(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/util/codec/codec.go b/util/codec/codec.go index 5709a1ceb5d69..419c268e2b814 100644 --- a/util/codec/codec.go +++ b/util/codec/codec.go @@ -635,6 +635,15 @@ func HashChunkSelected(sc *stmtctx.StatementContext, h []hash.Hash64, chk *chunk _, _ = h[i].Write(buf) _, _ = h[i].Write(b) } + case mysql.TypeNull: + for i := 0; i < rows; i++ { + if sel != nil && !sel[i] { + continue + } + isNull[i] = true + buf[0] = NilFlag + _, _ = h[i].Write(buf) + } default: return errors.Errorf("unsupport column type for encode %d", tp.Tp) } diff --git a/util/codec/codec_test.go b/util/codec/codec_test.go index 4f99c161a948a..5605609352612 100644 --- a/util/codec/codec_test.go +++ b/util/codec/codec_test.go @@ -959,6 +959,7 @@ func datumsForTest(sc *stmtctx.StatementContext) ([]types.Datum, []*types.FieldT value interface{} tp *types.FieldType }{ + {nil, types.NewFieldType(mysql.TypeNull)}, {nil, types.NewFieldType(mysql.TypeLonglong)}, {int64(1), types.NewFieldType(mysql.TypeTiny)}, {int64(1), types.NewFieldType(mysql.TypeShort)}, @@ -1103,6 +1104,7 @@ func (s *testCodecSuite) TestHashChunkRow(c *C) { c.Assert(err, IsNil) c.Assert(e, IsTrue) + testHashChunkRowEqual(c, nil, nil, true) testHashChunkRowEqual(c, uint64(1), int64(1), true) testHashChunkRowEqual(c, uint64(18446744073709551615), int64(-1), false) @@ -1174,35 +1176,38 @@ func (s *testCodecSuite) TestHashChunkColumns(c *C) { vecHash := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} rowHash := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} - // Test hash value of the first `Null` column - c.Assert(chk.GetRow(0).IsNull(0), Equals, true) - err1 := HashChunkColumns(sc, vecHash, chk, tps[0], 0, buf, hasNull) - err2 := HashChunkRow(sc, rowHash[0], chk.GetRow(0), tps, colIdx[0:1], buf) - err3 := HashChunkRow(sc, rowHash[1], chk.GetRow(1), tps, colIdx[0:1], buf) - err4 := HashChunkRow(sc, rowHash[2], chk.GetRow(2), tps, colIdx[0:1], buf) - c.Assert(err1, IsNil) - c.Assert(err2, IsNil) - c.Assert(err3, IsNil) - c.Assert(err4, IsNil) + // Test hash value of the first two `Null` columns + for i := 0; i < 2; i++ { + c.Assert(chk.GetRow(0).IsNull(i), Equals, true) + err1 := HashChunkColumns(sc, vecHash, chk, tps[i], i, buf, hasNull) + err2 := HashChunkRow(sc, rowHash[0], chk.GetRow(0), tps, colIdx[i:i+1], buf) + err3 := HashChunkRow(sc, rowHash[1], chk.GetRow(1), tps, colIdx[i:i+1], buf) + err4 := HashChunkRow(sc, rowHash[2], chk.GetRow(2), tps, colIdx[i:i+1], buf) + c.Assert(err1, IsNil) + c.Assert(err2, IsNil) + c.Assert(err3, IsNil) + c.Assert(err4, IsNil) - c.Assert(hasNull[0], Equals, true) - c.Assert(hasNull[1], Equals, true) - c.Assert(hasNull[2], Equals, true) - c.Assert(vecHash[0].Sum64(), Equals, rowHash[0].Sum64()) - c.Assert(vecHash[1].Sum64(), Equals, rowHash[1].Sum64()) - c.Assert(vecHash[2].Sum64(), Equals, rowHash[2].Sum64()) + c.Assert(hasNull[0], Equals, true) + c.Assert(hasNull[1], Equals, true) + c.Assert(hasNull[2], Equals, true) + c.Assert(vecHash[0].Sum64(), Equals, rowHash[0].Sum64()) + c.Assert(vecHash[1].Sum64(), Equals, rowHash[1].Sum64()) + c.Assert(vecHash[2].Sum64(), Equals, rowHash[2].Sum64()) + + } // Test hash value of every single column that is not `Null` - for i := 1; i < len(tps); i++ { + for i := 2; i < len(tps); i++ { hasNull = []bool{false, false, false} vecHash = []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} rowHash = []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} c.Assert(chk.GetRow(0).IsNull(i), Equals, false) - err1 = HashChunkColumns(sc, vecHash, chk, tps[i], i, buf, hasNull) - err2 = HashChunkRow(sc, rowHash[0], chk.GetRow(0), tps, colIdx[i:i+1], buf) - err3 = HashChunkRow(sc, rowHash[1], chk.GetRow(1), tps, colIdx[i:i+1], buf) - err4 = HashChunkRow(sc, rowHash[2], chk.GetRow(2), tps, colIdx[i:i+1], buf) + err1 := HashChunkColumns(sc, vecHash, chk, tps[i], i, buf, hasNull) + err2 := HashChunkRow(sc, rowHash[0], chk.GetRow(0), tps, colIdx[i:i+1], buf) + err3 := HashChunkRow(sc, rowHash[1], chk.GetRow(1), tps, colIdx[i:i+1], buf) + err4 := HashChunkRow(sc, rowHash[2], chk.GetRow(2), tps, colIdx[i:i+1], buf) c.Assert(err1, IsNil) c.Assert(err2, IsNil) c.Assert(err3, IsNil)