diff --git a/executor/join_test.go b/executor/join_test.go index 1e9ced2446229..e88f3b21249c4 100644 --- a/executor/join_test.go +++ b/executor/join_test.go @@ -237,6 +237,38 @@ func (s *testSuite) TestJoin(c *C) { tk.MustExec("insert into t values(1,2), (5,3), (6,4)") tk.MustExec("insert into t1 values(1), (2), (3)") tk.MustQuery("select /*+ TIDB_INLJ(t1) */ t1.a from t1, t where t.a = 5 and t.b = t1.a").Check(testkit.Rows("3")) + + // test issue#4997 + tk.MustExec("drop table if exists t1, t2") + tk.MustExec(` + CREATE TABLE t1 ( + pk int(11) NOT NULL AUTO_INCREMENT primary key, + a int(11) DEFAULT NULL, + b date DEFAULT NULL, + c varchar(1) DEFAULT NULL, + KEY a (a), + KEY b (b), + KEY c (c,a) + )`) + tk.MustExec(` + CREATE TABLE t2 ( + pk int(11) NOT NULL AUTO_INCREMENT primary key, + a int(11) DEFAULT NULL, + b date DEFAULT NULL, + c varchar(1) DEFAULT NULL, + KEY a (a), + KEY b (b), + KEY c (c,a) + )`) + tk.MustExec(`insert into t1 value(1,1,"2000-11-11", null);`) + result = tk.MustQuery(` + SELECT table2.b AS field2 FROM + ( + t1 AS table1 LEFT OUTER JOIN + (SELECT tmp_t2.* FROM ( t2 AS tmp_t1 RIGHT JOIN t1 AS tmp_t2 ON (tmp_t2.a = tmp_t1.a))) AS table2 + ON (table2.c = table1.c) + ) `) + result.Check(testkit.Rows("")) } func (s *testSuite) TestJoinCast(c *C) { diff --git a/plan/eliminate_projection.go b/plan/eliminate_projection.go index ff1aa328b2143..ded5452534bb3 100644 --- a/plan/eliminate_projection.go +++ b/plan/eliminate_projection.go @@ -18,6 +18,7 @@ import ( "github.com/pingcap/tidb/context" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/terror" + "github.com/pingcap/tidb/util/types" ) // canProjectionBeEliminatedLoose checks whether a projection can be eliminated, returns true if @@ -160,15 +161,53 @@ func (pe *projectionEliminater) eliminate(p LogicalPlan, replace map[string]*exp if !(isProj && canEliminate && canProjectionBeEliminatedLoose(proj)) { return p } - - child := p.Children()[0] + if join, ok := p.Parents()[0].(*LogicalJoin); ok { + pe.resetDefaultValues(join, p) + } exprs := proj.Exprs for i, col := range proj.Schema().Columns { replace[string(col.HashCode())] = exprs[i].(*expression.Column) } err := RemovePlan(p) terror.Log(errors.Trace(err)) - return child.(LogicalPlan) + return p.Children()[0].(LogicalPlan) +} + +// If the inner child of a Join is a Projection which been eliminated, +// and the schema of child plan of Projection is not consistent with +// the schema of Projection, the default values of Join should be reset. +func (pe *projectionEliminater) resetDefaultValues(join *LogicalJoin, prj Plan) { + prjChild := prj.Children()[0] + var joinInnerChild Plan + switch join.JoinType { + case LeftOuterJoin: + joinInnerChild = join.Children()[1] + case RightOuterJoin: + joinInnerChild = join.Children()[0] + default: + return + } + if joinInnerChild != prj { + return + } + var schemaIdxMap map[int]int + prjSchema := prj.Schema().Columns + childOfPrjSchema := prjChild.Schema().Columns + for i := 0; i < len(prjSchema); i++ { + for j := 0; j < len(childOfPrjSchema); j++ { + if prjSchema[i].Equal(childOfPrjSchema[j], nil) { + schemaIdxMap[i] = j + } + } + } + newDefaultValues := make([]types.Datum, len(childOfPrjSchema)) + for i := range prjSchema { + if j, ok := schemaIdxMap[i]; ok { + newDefaultValues[j] = join.DefaultValues[i] + } + } + join.DefaultValues = newDefaultValues + return } func (p *LogicalJoin) replaceExprColumns(replace map[string]*expression.Column) {