From f1805f3deebd41e0806f2931a2183bef09281a7f Mon Sep 17 00:00:00 2001 From: HuaiyuXu <391585975@qq.com> Date: Thu, 31 Dec 2020 13:39:49 +0800 Subject: [PATCH] planner: avoid using index_merge when there are multiple table filters (#22122) --- planner/core/integration_test.go | 48 ++++++++++++++++--- planner/core/stats.go | 10 ++++ .../core/testdata/integration_suite_in.json | 6 +++ .../core/testdata/integration_suite_out.json | 15 ++++++ 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go index c36cf1d6377c6..255edbe26d76f 100644 --- a/planner/core/integration_test.go +++ b/planner/core/integration_test.go @@ -1429,13 +1429,10 @@ func (s *testIntegrationSerialSuite) TestIndexMerge(c *C) { tk.MustQuery("show warnings").Check(testkit.Rows()) tk.MustQuery("desc select /*+ use_index_merge(t) */ * from t where (a=1 and length(b)=1) or (b=1 and length(a)=1)").Check(testkit.Rows( - "Projection_4 1.60 root test.t.a, test.t.b", - "└─IndexMerge_9 1.60 root ", - " ├─IndexRangeScan_5(Build) 1.00 cop[tikv] table:t, index:a(a) range:[1,1], keep order:false, stats:pseudo", - " ├─IndexRangeScan_6(Build) 1.00 cop[tikv] table:t, index:b(b) range:[1,1], keep order:false, stats:pseudo", - " └─Selection_8(Probe) 1.60 cop[tikv] eq(length(cast(test.t.a)), 1), eq(length(cast(test.t.b)), 1)", - " └─TableRowIDScan_7 2.00 cop[tikv] table:t keep order:false, stats:pseudo")) - tk.MustQuery("show warnings").Check(testkit.Rows()) + "TableReader_7 1.60 root data:Selection_6", + "└─Selection_6 1.60 cop[tikv] or(and(eq(test.t.a, 1), eq(length(cast(test.t.b)), 1)), and(eq(test.t.b, 1), eq(length(cast(test.t.a)), 1)))", + " └─TableFullScan_5 10000.00 cop[tikv] table:t keep order:false, stats:pseudo")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 IndexMerge is inapplicable or disabled")) } func (s *testIntegrationSerialSuite) TestIssue16407(c *C) { @@ -2358,3 +2355,40 @@ func (s *testIntegrationSuite) TestIssue22040(c *C) { c.Assert(errors.Cause(err), FitsTypeOf, expression.ErrOperandColumns) } } + +func (s *testIntegrationSuite) TestIssue22105(c *C) { + tk := testkit.NewTestKit(c, s.store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec(`CREATE TABLE t1 ( + key1 int(11) NOT NULL, + key2 int(11) NOT NULL, + key3 int(11) NOT NULL, + key4 int(11) NOT NULL, + key5 int(11) DEFAULT NULL, + key6 int(11) DEFAULT NULL, + key7 int(11) NOT NULL, + key8 int(11) NOT NULL, + KEY i1 (key1), + KEY i2 (key2), + KEY i3 (key3), + KEY i4 (key4), + KEY i5 (key5), + KEY i6 (key6) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin`) + + var input []string + var output []struct { + SQL string + Plan []string + } + s.testData.GetTestCases(c, &input, &output) + for i, tt := range input { + s.testData.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = s.testData.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} diff --git a/planner/core/stats.go b/planner/core/stats.go index 1e77480b8fe8e..ebe7e4f046417 100644 --- a/planner/core/stats.go +++ b/planner/core/stats.go @@ -426,6 +426,9 @@ func (ds *DataSource) generateIndexMergeOrPaths() { } if len(partialPaths) > 1 { possiblePath := ds.buildIndexMergeOrPath(partialPaths, i) + if possiblePath == nil { + return + } accessConds := make([]expression.Expression, 0, len(partialPaths)) for _, p := range partialPaths { @@ -554,8 +557,15 @@ func (ds *DataSource) buildIndexMergeOrPath(partialPaths []*util.AccessPath, cur indexMergePath := &util.AccessPath{PartialIndexPaths: partialPaths} indexMergePath.TableFilters = append(indexMergePath.TableFilters, ds.pushedDownConds[:current]...) indexMergePath.TableFilters = append(indexMergePath.TableFilters, ds.pushedDownConds[current+1:]...) + tableFilterCnt := 0 for _, path := range partialPaths { + // IndexMerge should not be used when the SQL is like 'select x from t WHERE (key1=1 AND key2=2) OR (key1=4 AND key3=6);'. + // Check issue https://github.com/pingcap/tidb/issues/22105 for details. if len(path.TableFilters) > 0 { + tableFilterCnt++ + if tableFilterCnt > 1 { + return nil + } indexMergePath.TableFilters = append(indexMergePath.TableFilters, path.TableFilters...) } } diff --git a/planner/core/testdata/integration_suite_in.json b/planner/core/testdata/integration_suite_in.json index 4525e01818612..c01d844bc2a0c 100644 --- a/planner/core/testdata/integration_suite_in.json +++ b/planner/core/testdata/integration_suite_in.json @@ -242,5 +242,11 @@ "explain select * from t2 where a >= 2.5 and a <= 2.5 order by b limit 2", "explain select * from t3 where a >= 'a' and a <= 'a' and b = 'b' and c > 'c'" ] + }, + { + "name": "TestIssue22105", + "cases": [ + "explain SELECT /*+ use_index_merge(t1)*/ COUNT(*) FROM t1 WHERE (key4=42 AND key6 IS NOT NULL) OR (key1=4 AND key3=6)" + ] } ] diff --git a/planner/core/testdata/integration_suite_out.json b/planner/core/testdata/integration_suite_out.json index 016af2817a3dc..e7388c9e58140 100644 --- a/planner/core/testdata/integration_suite_out.json +++ b/planner/core/testdata/integration_suite_out.json @@ -1348,5 +1348,20 @@ ] } ] + }, + { + "Name": "TestIssue22105", + "Cases": [ + { + "SQL": "explain SELECT /*+ use_index_merge(t1)*/ COUNT(*) FROM t1 WHERE (key4=42 AND key6 IS NOT NULL) OR (key1=4 AND key3=6)", + "Plan": [ + "StreamAgg_20 1.00 root funcs:count(Column#12)->Column#10", + "└─TableReader_21 1.00 root data:StreamAgg_9", + " └─StreamAgg_9 1.00 cop[tikv] funcs:count(1)->Column#12", + " └─Selection_19 10.00 cop[tikv] or(and(eq(test.t1.key4, 42), not(isnull(test.t1.key6))), and(eq(test.t1.key1, 4), eq(test.t1.key3, 6)))", + " └─TableFullScan_18 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" + ] + } + ] } ]