Repository: pingcap/tidb (Go)

Issue:
`Can't find a proper physical plan for this query` when hint using primary index and tiflash replica exists
## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)

```sql
create table tt(a int primary key, b int);
alter table tt set tiflash replica 1;
explain select * from tt;
explain select * from tt use index (primary);
```
```
> create table tt(a int primary key, b int);
Query OK, 0 rows affected (0.074 sec)

> alter table tt set tiflash replica 1;
Query OK, 0 rows affected (0.062 sec)

> explain select * from tt;
+-----------------------+----------+--------------+---------------+--------------------------------+
| id                    | estRows  | task         | access object | operator info                  |
+-----------------------+----------+--------------+---------------+--------------------------------+
| TableReader_7         | 10000.00 | root         |               | data:TableFullScan_6           |
| └─TableFullScan_6     | 10000.00 | cop[tiflash] | table:tt      | keep order:false, stats:pseudo |
+-----------------------+----------+--------------+---------------+--------------------------------+
2 rows in set (0.000 sec)

> explain select * from tt use index (primary);
ERROR 1815 (HY000): Internal : Can't find a proper physical plan for this query
```

### 2. What did you expect to see? (Required)

Generate a correct plan.

### 3. What did you see instead (Required)

```
ERROR 1815 (HY000): Internal : Can't find a proper physical plan for this query
```

### 4. What is your TiDB version? (Required)
master




Discussion:
Please check whether the issue should be labeled with 'affects-x.y' or 'fixes-x.y.z', and then remove 'needs-more-info' label.



Patch:
diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go
index 6b736eca3e5cf..0dfd7ea71c743 100644
--- a/planner/core/planbuilder.go
+++ b/planner/core/planbuilder.go
@@ -952,10 +952,13 @@ func (b *PlanBuilder) detectSelectWindow(sel *ast.SelectStmt) bool {
 }
 
 func getPathByIndexName(paths []*util.AccessPath, idxName model.CIStr, tblInfo *model.TableInfo) *util.AccessPath {
-	var tablePath *util.AccessPath
+	var primaryIdxPath *util.AccessPath
 	for _, path := range paths {
+		if path.StoreType == kv.TiFlash {
+			continue
+		}
 		if path.IsTablePath() {
-			tablePath = path
+			primaryIdxPath = path
 			continue
 		}
 		if path.Index.Name.L == idxName.L {
@@ -963,7 +966,7 @@ func getPathByIndexName(paths []*util.AccessPath, idxName model.CIStr, tblInfo *
 		}
 	}
 	if isPrimaryIndex(idxName) && tblInfo.HasClusteredIndex() {
-		return tablePath
+		return primaryIdxPath
 	}
 	return nil
 }


Test patch:
diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go
index f9f3344aced12..a4cacd0d58f17 100644
--- a/planner/core/integration_test.go
+++ b/planner/core/integration_test.go
@@ -5219,26 +5219,55 @@ func (s *testIntegrationSuite) TestDNFCondSelectivityWithConst(c *C) {
 	testKit.MustExec("insert into t1 value(1,1,1)")
 	testKit.MustExec("analyze table t1")
 
-	testKit.MustQuery("explain select * from t1 where a=1 or b=1;").Check(testkit.Rows("TableReader_7 1.99 root  data:Selection_6]\n" +
-		"[└─Selection_6 1.99 cop[tikv]  or(eq(test.t1.a, 1), eq(test.t1.b, 1))]\n" +
-		"[  └─TableFullScan_5 129.00 cop[tikv] table:t1 keep order:false"))
-	testKit.MustQuery("explain select * from t1 where 0=1 or a=1 or b=1;").Check(testkit.Rows("TableReader_7 1.99 root  data:Selection_6]\n" +
-		"[└─Selection_6 1.99 cop[tikv]  or(0, or(eq(test.t1.a, 1), eq(test.t1.b, 1)))]\n" +
-		"[  └─TableFullScan_5 129.00 cop[tikv] table:t1 keep order:false"))
-	testKit.MustQuery("explain select * from t1 where null or a=1 or b=1;").Check(testkit.Rows("TableReader_7 1.99 root  data:Selection_6]\n" +
-		"[└─Selection_6 1.99 cop[tikv]  or(0, or(eq(test.t1.a, 1), eq(test.t1.b, 1)))]\n" +
-		"[  └─TableFullScan_5 129.00 cop[tikv] table:t1 keep order:false"))
-	testKit.MustQuery("explain select * from t1 where a=1 or false or b=1;").Check(testkit.Rows("TableReader_7 1.99 root  data:Selection_6]\n" +
-		"[└─Selection_6 1.99 cop[tikv]  or(eq(test.t1.a, 1), or(0, eq(test.t1.b, 1)))]\n" +
-		"[  └─TableFullScan_5 129.00 cop[tikv] table:t1 keep order:false"))
-	testKit.MustQuery("explain select * from t1 where a=1 or b=1 or \"false\";").Check(testkit.Rows("TableReader_7 1.99 root  data:Selection_6]\n" +
-		"[└─Selection_6 1.99 cop[tikv]  or(eq(test.t1.a, 1), or(eq(test.t1.b, 1), 0))]\n" +
-		"[  └─TableFullScan_5 129.00 cop[tikv] table:t1 keep order:false"))
-	testKit.MustQuery("explain select * from t1 where 1=1 or a=1 or b=1;").Check(testkit.Rows("TableReader_7 129.00 root  data:Selection_6]\n" +
-		"[└─Selection_6 129.00 cop[tikv]  or(1, or(eq(test.t1.a, 1), eq(test.t1.b, 1)))]\n" +
-		"[  └─TableFullScan_5 129.00 cop[tikv] table:t1 keep order:false"))
-	testKit.MustQuery("explain select * from t1 where a=1 or b=1 or 1=1;").Check(testkit.Rows("TableReader_7 129.00 root  data:Selection_6]\n" +
-		"[└─Selection_6 129.00 cop[tikv]  or(eq(test.t1.a, 1), or(eq(test.t1.b, 1), 1))]\n" +
-		"[  └─TableFullScan_5 129.00 cop[tikv] table:t1 keep order:false"))
+	testKit.MustQuery("explain format = 'brief' select * from t1 where a=1 or b=1;").Check(testkit.Rows(
+		"TableReader 1.99 root  data:Selection",
+		"└─Selection 1.99 cop[tikv]  or(eq(test.t1.a, 1), eq(test.t1.b, 1))",
+		"  └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
+	testKit.MustQuery("explain format = 'brief' select * from t1 where 0=1 or a=1 or b=1;").Check(testkit.Rows(
+		"TableReader 1.99 root  data:Selection",
+		"└─Selection 1.99 cop[tikv]  or(0, or(eq(test.t1.a, 1), eq(test.t1.b, 1)))",
+		"  └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
+	testKit.MustQuery("explain format = 'brief' select * from t1 where null or a=1 or b=1;").Check(testkit.Rows(
+		"TableReader 1.99 root  data:Selection",
+		"└─Selection 1.99 cop[tikv]  or(0, or(eq(test.t1.a, 1), eq(test.t1.b, 1)))",
+		"  └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
+	testKit.MustQuery("explain format = 'brief' select * from t1 where a=1 or false or b=1;").Check(testkit.Rows(
+		"TableReader 1.99 root  data:Selection",
+		"└─Selection 1.99 cop[tikv]  or(eq(test.t1.a, 1), or(0, eq(test.t1.b, 1)))",
+		"  └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
+	testKit.MustQuery("explain format = 'brief' select * from t1 where a=1 or b=1 or \"false\";").Check(testkit.Rows(
+		"TableReader 1.99 root  data:Selection",
+		"└─Selection 1.99 cop[tikv]  or(eq(test.t1.a, 1), or(eq(test.t1.b, 1), 0))",
+		"  └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
+	testKit.MustQuery("explain format = 'brief' select * from t1 where 1=1 or a=1 or b=1;").Check(testkit.Rows(
+		"TableReader 129.00 root  data:Selection",
+		"└─Selection 129.00 cop[tikv]  or(1, or(eq(test.t1.a, 1), eq(test.t1.b, 1)))",
+		"  └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
+	testKit.MustQuery("explain format = 'brief' select * from t1 where a=1 or b=1 or 1=1;").Check(testkit.Rows(
+		"TableReader 129.00 root  data:Selection",
+		"└─Selection 129.00 cop[tikv]  or(eq(test.t1.a, 1), or(eq(test.t1.b, 1), 1))",
+		"  └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
 	testKit.MustExec("drop table if exists t1")
 }
+
+func (s *testIntegrationSuite) TestIssue31202(c *C) {
+	store, dom := s.store, s.dom
+	tk := testkit.NewTestKit(c, store)
+
+	tk.MustExec("use test")
+	tk.MustExec("create table t31202(a int primary key, b int);")
+
+	tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t31202", L: "t31202"})
+	c.Assert(err, IsNil)
+	// Set the hacked TiFlash replica for explain tests.
+	tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true}
+
+	tk.MustQuery("explain format = 'brief' select * from t31202;").Check(testkit.Rows(
+		"TableReader 10000.00 root  data:TableFullScan",
+		"└─TableFullScan 10000.00 cop[tiflash] table:t31202 keep order:false, stats:pseudo"))
+
+	tk.MustQuery("explain format = 'brief' select * from t31202 use index (primary);").Check(testkit.Rows(
+		"TableReader 10000.00 root  data:TableFullScan",
+		"└─TableFullScan 10000.00 cop[tikv] table:t31202 keep order:false, stats:pseudo"))
+	tk.MustExec("drop table if exists t31202")
+}
diff --git a/planner/core/planbuilder_test.go b/planner/core/planbuilder_test.go
index b820da05ed9b0..05f8bf37e5838 100644
--- a/planner/core/planbuilder_test.go
+++ b/planner/core/planbuilder_test.go
@@ -92,6 +92,8 @@ func (s *testPlanBuilderSuite) TestGetPathByIndexName(c *C) {
 	accessPath := []*util.AccessPath{
 		{IsIntHandlePath: true},
 		{Index: &model.IndexInfo{Name: model.NewCIStr("idx")}},
+		genTiFlashPath(tblInfo, false),
+		genTiFlashPath(tblInfo, true),
 	}
 
 	path := getPathByIndexName(accessPath, model.NewCIStr("idx"), tblInfo)