Skip to content

Commit

Permalink
plan/executor: handle optimizer hint properly for cartesian join (#9037
Browse files Browse the repository at this point in the history
…) (#9074)
  • Loading branch information
eurekaka authored and zz-jason committed Jan 15, 2019
1 parent a2787e8 commit 7ded464
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 5 deletions.
2 changes: 1 addition & 1 deletion executor/merge_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ type mergeJoinInnerTable struct {
}

func (t *mergeJoinInnerTable) init(ctx context.Context, chk4Reader *chunk.Chunk) (err error) {
if t.reader == nil || t.joinKeys == nil || len(t.joinKeys) == 0 || ctx == nil {
if t.reader == nil || ctx == nil {
return errors.Errorf("Invalid arguments: Empty arguments detected.")
}
t.ctx = ctx
Expand Down
20 changes: 19 additions & 1 deletion executor/merge_join_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ func checkPlanAndRun(tk *testkit.TestKit, c *C, plan string, sql string) *testki
}

func (s *testSuite) TestMergeJoin(c *C) {
// FIXME: the TIDB_SMJ hint does not really work when there is no index on join onCondition.
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")

Expand Down Expand Up @@ -331,6 +330,25 @@ func (s *testSuite) TestMergeJoin(c *C) {
tk.MustExec("create table s(a int, primary key(a))")
tk.MustExec("insert into s value(1)")
tk.MustQuery("select /*+ TIDB_SMJ(t, s) */ count(*) from t join s on t.a = s.a").Check(testkit.Rows("4"))

// Test TIDB_SMJ for cartesian product.
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int)")
tk.MustExec("insert into t value(1),(2)")
tk.MustQuery("explain select /*+ TIDB_SMJ(t1, t2) */ * from t t1 join t t2 order by t1.a, t2.a").Check(testkit.Rows(
"Sort_6 100000000.00 root t1.a:asc, t2.a:asc",
"└─MergeJoin_9 100000000.00 root inner join",
" ├─TableReader_11 10000.00 root data:TableScan_10",
" │ └─TableScan_10 10000.00 cop table:t1, range:[-inf,+inf], keep order:false, stats:pseudo",
" └─TableReader_13 10000.00 root data:TableScan_12",
" └─TableScan_12 10000.00 cop table:t2, range:[-inf,+inf], keep order:false, stats:pseudo",
))
tk.MustQuery("select /*+ TIDB_SMJ(t1, t2) */ * from t t1 join t t2 order by t1.a, t2.a").Check(testkit.Rows(
"1 1",
"1 2",
"2 1",
"2 2",
))
}

func (s *testSuite) Test3WaysMergeJoin(c *C) {
Expand Down
10 changes: 7 additions & 3 deletions planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,12 +588,16 @@ func (p *LogicalJoin) buildFakeEqCondsForIndexJoin(keys, idxCols []*expression.C
// tryToGetIndexJoin will get index join by hints. If we can generate a valid index join by hint, the second return value
// will be true, which means we force to choose this index join. Otherwise we will select a join algorithm with min-cost.
func (p *LogicalJoin) tryToGetIndexJoin(prop *property.PhysicalProperty) ([]PhysicalPlan, bool) {
if len(p.EqualConditions) == 0 {
return nil, false
}
plans := make([]PhysicalPlan, 0, 2)
rightOuter := (p.preferJoinType & preferLeftAsIndexInner) > 0
leftOuter := (p.preferJoinType & preferRightAsIndexInner) > 0
if len(p.EqualConditions) == 0 {
if leftOuter || rightOuter {
warning := ErrInternal.GenWithStack("TIDB_INLJ hint is inapplicable without column equal ON condition")
p.ctx.GetSessionVars().StmtCtx.AppendWarning(warning)
}
return nil, false
}
switch p.JoinType {
case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin, LeftOuterJoin:
join := p.getIndexJoinByOuterIdx(prop, 0)
Expand Down
25 changes: 25 additions & 0 deletions planner/core/physical_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/session"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/terror"
"github.com/pingcap/tidb/util/testleak"
"golang.org/x/net/context"
)
Expand Down Expand Up @@ -1280,3 +1281,27 @@ func (s *testPlanSuite) TestRequestTypeSupportedOff(c *C) {
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, expect, Commentf("for %s", sql))
}

func (s *testPlanSuite) TestIndexLookupCartesianJoin(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
sql := "select /*+ TIDB_INLJ(t1, t2) */ * from t t1 join t t2"
stmt, err := s.ParseOneStmt(sql, "", "")
c.Assert(err, IsNil)
p, err := core.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}")
warnings := se.GetSessionVars().StmtCtx.GetWarnings()
lastWarn := warnings[len(warnings)-1]
err = core.ErrInternal.GenWithStack("TIDB_INLJ hint is inapplicable without column equal ON condition")
c.Assert(terror.ErrorEqual(err, lastWarn.Err), IsTrue)
}

0 comments on commit 7ded464

Please sign in to comment.