Repository: pingcap/tidb (Go)

Issue:
`explain format='hint'`'s ouput is not recoverable.
## Description
## Bug Report


### 1. Minimal reproduce step (Required)
Create a cluster that contains one `TiFlash` node.

```sql
> create table t(a int, index idx(a))
> ALTER TABLE t SET TIFLASH REPLICA 1

> explain select /*+ read_from_storage(tiflash[t]) */ * from t;
+-------------------+----------+--------------+---------------+--------------------------------+
| id                | estRows  | task         | access object | operator info                  |
+-------------------+----------+--------------+---------------+--------------------------------+
| TableReader_5     | 10000.00 | root         |               | data:TableFullScan_4           |
| └─TableFullScan_4 | 10000.00 | cop[tiflash] | table:t       | keep order:false, stats:pseudo |
+-------------------+----------+--------------+---------------+--------------------------------+


> explain format='hint' select /*+ read_from_storage(tiflash[t]) */ * from t;
+-------------------------------------------------------------------------------------------------------------------+
| hint                                                                                                              |
+-------------------------------------------------------------------------------------------------------------------+
| use_index(@`sel_1` `test`.`t` ), read_from_storage(@`sel_1` tiflash[`test`.`t`]), read_from_storage(tiflash[`t`]) |
+-------------------------------------------------------------------------------------------------------------------+

> /* use this output hint to construct another hint */

> explain select /*+ use_index(@`sel_1` `test`.`t` ), read_from_storage(@`sel_1` tiflash[`test`.`t`]), read_from_storage(tiflash[`t`]) */ * from t;

+-------------------+----------+-----------+---------------+--------------------------------+
| id                | estRows  | task      | access object | operator info                  |
+-------------------+----------+-----------+---------------+--------------------------------+
| TableReader_5     | 10000.00 | root      |               | data:TableFullScan_4           |
| └─TableFullScan_4 | 10000.00 | cop[tikv] | table:t       | keep order:false, stats:pseudo |
+-------------------+----------+-----------+---------------+--------------------------------+
```

And here is the warnings:
```sql
+---------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                                                                                                                                                  |
+---------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1815 | No available path for table test.t with the store type tiflash of the hint /*+ read_from_storage */, please check the status of the table replica and variable value of tidb_isolation_read_engines(map[0:{} 1:{} 2:{}]) |
| Warning | 1815 | There are no matching table names for (t) in optimizer hint /*+ READ_FROM_STORAGE(tiflash[t, t]) */. Maybe you can use the table alias name                                                                              |
+---------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

```

### 2. What did you expect to see? (Required)

```sql
> explain select /*+ use_index(@`sel_1` `test`.`t` ), read_from_storage(@`sel_1` tiflash[`test`.`t`]), read_from_storage(tiflash[`t`]) */ * from t;
+-------------------+----------+--------------+---------------+--------------------------------+
| id                | estRows  | task         | access object | operator info                  |
+-------------------+----------+--------------+---------------+--------------------------------+
| TableReader_5     | 10000.00 | root         |               | data:TableFullScan_4           |
| └─TableFullScan_4 | 10000.00 | cop[tiflash] | table:t       | keep order:false, stats:pseudo |
+-------------------+----------+--------------+---------------+--------------------------------+
```

### 3. What did you see instead (Required)

```sql
> explain select /*+ use_index(@`sel_1` `test`.`t` ), read_from_storage(@`sel_1` tiflash[`test`.`t`]), read_from_storage(tiflash[`t`]) */ * from t;

+-------------------+----------+-----------+---------------+--------------------------------+
| id                | estRows  | task      | access object | operator info                  |
+-------------------+----------+-----------+---------------+--------------------------------+
| TableReader_5     | 10000.00 | root      |               | data:TableFullScan_4           |
| └─TableFullScan_4 | 10000.00 | cop[tikv] | table:t       | keep order:false, stats:pseudo |
+-------------------+----------+-----------+---------------+--------------------------------+
```

### 4. What is your TiDB version? (Required)

`Master`

## SIG slack channel

[#sig-planner](https://slack.tidb.io/invite?team=tidb-community&channel=sig-planner&ref=high-performance)

## Score

- 300

## Mentor

* @lzmhhh123


Discussion:
/pick-up
Pick up success.
/give-up
Give up success.
The issue has been removed from the challenge program.
## Please edit this comment or add a new comment to complete the following information

### Not a bug

1. Remove the 'type/bug' label
2. Add notes to indicate why it is not a bug

### Duplicate bug

1. Add the 'type/duplicate' label
2. Add the link to the original bug

### Bug

Note: Make Sure that 'component', and 'severity' labels are added
Example for how to fill out the template: https://github.com/pingcap/tidb/issues/20100

#### 1. Root Cause Analysis (RCA) (optional) 
<!-- Write down the reason why this bug occurs -->

#### 2. Symptom (optional)

<!-- What will the user see when this bug occurs. The error message may be in the terminal, log or monitoring -->

#### 3. All Trigger Conditions (optional)

<!-- All the user scenarios that may trigger this bug -->

#### 4. Workaround (optional)

#### 5. Affected versions

<!--
In the format of [start_version:end_version], multiple version ranges are
accepted. If the bug only affects the unreleased version, please input:
"unreleased". For example:

Notes:
  1. Do not use any white spaces in '[]'.
  2. The range in '[]' is a closed interval
  3. The version format is `v$Major.$Minor.$Patch`, the $Majoy and $Minor
     number in a version range should be the same. [v3.0.1:v3.1.2] is
     invalid because the $Minor number of the version range is different.

Example 1: [v3.0.1:v3.0.5], [v4.0.1:v4.0.5]
Example 2: unreleased
-->

#### 6. Fixed versions

<!--
The first released version that contains this fix in each minor version. If the bug's affected version has been released, the fixed version should be a detailed version number; If the bug doesn't affect any released version, the fixed version can be "master";  

Example 1: v3.0.13, v4.0.5
Example 2: master
-->



Patch:
diff --git a/planner/core/hints.go b/planner/core/hints.go
index d0c867068862f..cddd36d5caf30 100644
--- a/planner/core/hints.go
+++ b/planner/core/hints.go
@@ -131,11 +131,6 @@ func genHintsFromPhysicalPlan(p PhysicalPlan, nodeType utilhint.NodeType) (res [
 	switch pp := p.(type) {
 	case *PhysicalTableReader:
 		tbl := pp.TablePlans[0].(*PhysicalTableScan)
-		res = append(res, &ast.TableOptimizerHint{
-			QBName:   qbName,
-			HintName: model.NewCIStr(HintUseIndex),
-			Tables:   []ast.HintTable{{DBName: tbl.DBName, TableName: getTableName(tbl.Table.Name, tbl.TableAsName)}},
-		})
 		if tbl.StoreType == kv.TiFlash {
 			res = append(res, &ast.TableOptimizerHint{
 				QBName:   qbName,
@@ -143,6 +138,12 @@ func genHintsFromPhysicalPlan(p PhysicalPlan, nodeType utilhint.NodeType) (res [
 				HintData: model.NewCIStr(kv.TiFlash.Name()),
 				Tables:   []ast.HintTable{{DBName: tbl.DBName, TableName: getTableName(tbl.Table.Name, tbl.TableAsName)}},
 			})
+		} else {
+			res = append(res, &ast.TableOptimizerHint{
+				QBName:   qbName,
+				HintName: model.NewCIStr(HintUseIndex),
+				Tables:   []ast.HintTable{{DBName: tbl.DBName, TableName: getTableName(tbl.Table.Name, tbl.TableAsName)}},
+			})
 		}
 	case *PhysicalIndexLookUpReader:
 		index := pp.IndexPlans[0].(*PhysicalIndexScan)


Test patch:
diff --git a/planner/core/plan_test.go b/planner/core/plan_test.go
index 43b599ac7eaa3..a7cb71136470b 100644
--- a/planner/core/plan_test.go
+++ b/planner/core/plan_test.go
@@ -433,6 +433,36 @@ func (s *testPlanNormalize) TestExplainFormatHint(c *C) {
 		"use_index(@`sel_2` `test`.`t2` `idx_c2`), hash_agg(@`sel_2`), use_index(@`sel_1` `test`.`t1` `idx_c2`), hash_agg(@`sel_1`)"))
 }
 
+func (s *testPlanNormalize) TestExplainFormatHintRecoverableForTiFlashReplica(c *C) {
+	tk := testkit.NewTestKit(c, s.store)
+	tk.MustExec("use test")
+	tk.MustExec("drop table if exists t")
+	tk.MustExec("create table t(a int)")
+	// Create virtual `tiflash` replica info.
+	dom := domain.GetDomain(tk.Se)
+	is := dom.InfoSchema()
+	db, exists := is.SchemaByName(model.NewCIStr("test"))
+	c.Assert(exists, IsTrue)
+	for _, tblInfo := range db.Tables {
+		if tblInfo.Name.L == "t" {
+			tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{
+				Count:     1,
+				Available: true,
+			}
+		}
+	}
+
+	rows := tk.MustQuery("explain select * from t").Rows()
+	c.Assert(rows[len(rows)-1][2], Equals, "cop[tiflash]")
+
+	rows = tk.MustQuery("explain format='hint' select * from t").Rows()
+	c.Assert(rows[0][0], Equals, "read_from_storage(@`sel_1` tiflash[`test`.`t`])")
+
+	hints := tk.MustQuery("explain format='hint' select * from t;").Rows()[0][0]
+	rows = tk.MustQuery(fmt.Sprintf("explain select /*+ %s */ * from t", hints)).Rows()
+	c.Assert(rows[len(rows)-1][2], Equals, "cop[tiflash]")
+}
+
 func (s *testPlanNormalize) TestNthPlanHint(c *C) {
 	tk := testkit.NewTestKit(c, s.store)
 	tk.MustExec("use test")