Repository: pingcap/tidb (Go)

Issue:
TiDB throw `invalid memory address or nil pointer dereference` error when clustered index is enabled.
## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)
The error can be reproduced using the file attached below.

[error4.txt](https://github.com/pingcap/tidb/files/6105497/error4.txt)

<!-- a step by step guide for reproducing the bug. -->

### 2. What did you expect to see? (Required)

sql finished without error
### 3. What did you see instead (Required)
```
[2021/03/09 10:25:16.867 +08:00] [ERROR] [conn.go:733] ["connection running loop panic"] [conn=7] [lastSQL="execute prepare_1"] [err="runtime error: invalid memory address or nil pointer dereference"] [stack="goroutine 2587 [running]:
github.com/pingcap/tidb/server.(*clientConn).Run.func1(0x6b76e40, 0xc0029ed2f0, 0xc00128d000)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/server/conn.go:731 +0xee
panic(0x6144cc0, 0x87f20b0)
        /usr/local/go/src/runtime/panic.go:969 +0x166
github.com/pingcap/tidb/util/ranger.(*rangeDetacher).detachCondAndBuildRangeForCols(0xc000de7b50, 0x1, 0x20, 0xc000580400)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/util/ranger/detacher.go:633 +0x102
github.com/pingcap/tidb/util/ranger.DetachCondAndBuildRangeForIndex(0x6bcae00, 0xc00128d100, 0x0, 0x0, 0x0, 0xc0000104c0, 0x1, 0x1, 0xc002985ae0, 0x1, ...)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/util/ranger/detacher.go:618 +0xf0
github.com/pingcap/tidb/planner/core.(*Execute).rebuildRange(0xc002bcb040, 0x6bae640, 0xc0019f6000, 0x6bae640, 0xc0019f6000)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/planner/core/common_plans.go:456 +0x16ec
github.com/pingcap/tidb/planner/core.(*Execute).rebuildRange(0xc002bcb040, 0x6badac0, 0xc00020e340, 0x88aa500, 0x0)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/planner/core/common_plans.go:588 +0xb0d
github.com/pingcap/tidb/planner/core.(*Execute).getPhysicalPlan(0xc002bcb040, 0x6b76e40, 0xc0023f22a0, 0x6bcae00, 0xc00128d100, 0x6bbe040, 0xc001843090, 0xc00035fb00, 0x756ea186be1590, 0x4fe623e8a1d78f4c)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/planner/core/common_plans.go:340 +0xd06
github.com/pingcap/tidb/planner/core.(*Execute).OptimizePreparedPlan(0xc002bcb040, 0x6b76e40, 0xc0023f22a0, 0x6bcae00, 0xc00128d100, 0x6bbe040, 0xc001843090, 0x0, 0x0)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/planner/core/common_plans.go:252 +0x549
github.com/pingcap/tidb/planner.optimize(0x6b76e40, 0xc0023f22a0, 0x6bcae00, 0xc00128d100, 0x6b925e0, 0xc0027c44e0, 0x6bbe040, 0xc001843090, 0x0, 0x0, ...)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/planner/optimize.go:256 +0x8dd
github.com/pingcap/tidb/planner.Optimize(0x6b76e40, 0xc0023f22a0, 0x6bcae00, 0xc00128d100, 0x6b925e0, 0xc0027c44e0, 0x6bbe040, 0xc001843090, 0x0, 0x0, ...)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/planner/optimize.go:123 +0x491
github.com/pingcap/tidb/executor.(*Compiler).Compile(0xc000de8db8, 0x6b76e40, 0xc0023f22a0, 0x6b9b640, 0xc0027c44e0, 0x0, 0x0, 0x0)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/executor/compiler.go:62 +0x2fd
github.com/pingcap/tidb/session.(*session).ExecuteStmt(0xc00128d100, 0x6b76e40, 0xc0023f20f0, 0x6b9b640, 0xc0027c44e0, 0x0, 0x0, 0x0, 0x0)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/session/session.go:1402 +0x3ca
github.com/pingcap/tidb/server.(*TiDBContext).ExecuteStmt(0xc0029ed7a0, 0x6b76e40, 0xc0023f20f0, 0x6b9b640, 0xc0027c44e0, 0xc002f1ab70, 0x6b76e40, 0xc0023f20f0, 0x406f101)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/server/driver_tidb.go:212 +0x68
github.com/pingcap/tidb/server.(*clientConn).handleStmt(0xc00128d000, 0x6b76e40, 0xc0023f20f0, 0x6b9b640, 0xc0027c44e0, 0x88aa518, 0x0, 0x0, 0x1, 0x0, ...)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/server/conn.go:1607 +0x141
github.com/pingcap/tidb/server.(*clientConn).handleQuery(0xc00128d000, 0x6b76d80, 0xc00247c440, 0xc002717421, 0x11, 0x0, 0x0)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/server/conn.go:1481 +0x4a5
github.com/pingcap/tidb/server.(*clientConn).dispatch(0xc00128d000, 0x6b76d80, 0xc00247c440, 0xc002717420, 0x12, 0x11, 0x0, 0x0)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/server/conn.go:1027 +0x696
github.com/pingcap/tidb/server.(*clientConn).Run(0xc00128d000, 0x6b76e40, 0xc0029ed2f0)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/server/conn.go:792 +0x29c
github.com/pingcap/tidb/server.(*Server).onConn(0xc00018e000, 0xc00128d000)
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/server/server.go:474 +0xa97
created by github.com/pingcap/tidb/server.(*Server).Run
        /Users/xufei/dev/go/src/github.com/pingcap/tidb/server/server.go:377 +0x8a5
```

### 4. What is your TiDB version? (Required)

<!-- Paste the output of SELECT tidb_version() -->
```
mysql> select tidb_version();
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tidb_version()                                                                                                                                                                                                                                                                                                              |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Release Version: v4.0.0-beta.2-2300-gd29febf93
Edition: Community
Git Commit Hash: d29febf93fe22ddd9e342d855accc9da045e22c9
Git Branch: master
UTC Build Time: 2021-03-08 09:58:36
GoVersion: go1.14.9
Race Enabled: false
TiKV Min Version: v3.0.0-60965b006877ca7234adaced7890d7b029ed1306
Check Table Before Drop: false |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
```




Discussion:
## 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
master
<!--
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
master
<!--
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/common_plans.go b/planner/core/common_plans.go
index 06c8d8c51dcc2..8b856d10dffa6 100644
--- a/planner/core/common_plans.go
+++ b/planner/core/common_plans.go
@@ -444,21 +444,27 @@ func (e *Execute) rebuildRange(p Plan) error {
 	switch x := p.(type) {
 	case *PhysicalTableReader:
 		ts := x.TablePlans[0].(*PhysicalTableScan)
-		var pkCol *expression.Column
 		if ts.Table.IsCommonHandle {
 			pk := tables.FindPrimaryIndex(ts.Table)
 			pkCols := make([]*expression.Column, 0, len(pk.Columns))
 			pkColsLen := make([]int, 0, len(pk.Columns))
 			for _, colInfo := range pk.Columns {
-				pkCols = append(pkCols, expression.ColInfo2Col(ts.schema.Columns, ts.Table.Columns[colInfo.Offset]))
-				pkColsLen = append(pkColsLen, colInfo.Length)
+				if pkCol := expression.ColInfo2Col(ts.schema.Columns, ts.Table.Columns[colInfo.Offset]); pkCol != nil {
+					pkCols = append(pkCols, pkCol)
+					pkColsLen = append(pkColsLen, colInfo.Length)
+				}
 			}
-			res, err := ranger.DetachCondAndBuildRangeForIndex(p.SCtx(), ts.AccessCondition, pkCols, pkColsLen)
-			if err != nil {
-				return err
+			if len(pkCols) > 0 {
+				res, err := ranger.DetachCondAndBuildRangeForIndex(p.SCtx(), ts.AccessCondition, pkCols, pkColsLen)
+				if err != nil {
+					return err
+				}
+				ts.Ranges = res.Ranges
+			} else {
+				ts.Ranges = ranger.FullRange()
 			}
-			ts.Ranges = res.Ranges
 		} else {
+			var pkCol *expression.Column
 			if ts.Table.PKIsHandle {
 				if pkColInfo := ts.Table.GetPkColInfo(); pkColInfo != nil {
 					pkCol = expression.ColInfo2Col(ts.schema.Columns, pkColInfo)


Test patch:
diff --git a/planner/core/prepare_test.go b/planner/core/prepare_test.go
index fc71286daeefe..6cdc6af508c56 100644
--- a/planner/core/prepare_test.go
+++ b/planner/core/prepare_test.go
@@ -426,6 +426,25 @@ func (s *testPrepareSuite) TestPrepareForGroupByItems(c *C) {
 	tk.MustQuery("execute s1 using @a;").Check(testkit.Rows("3"))
 }
 
+func (s *testPrepareSuite) TestPrepareCacheForClusteredIndex(c *C) {
+	defer testleak.AfterTest(c)()
+	store, dom, err := newStoreWithBootstrap()
+	c.Assert(err, IsNil)
+	tk := testkit.NewTestKit(c, store)
+	defer func() {
+		dom.Close()
+		store.Close()
+	}()
+	tk.MustExec("use test")
+	tk.MustExec("create table t1(k varchar(100) primary key clustered, v1 int, v2 int)")
+	tk.MustExec("insert into t1 (k, v1, v2) values('a', 1, 2), ('b', 1, 1)")
+	tk.MustExec("create table t2(k varchar(100) primary key clustered, v int)")
+	tk.MustExec("insert into t2 (k, v) values('c', 100)")
+	tk.MustExec(`prepare prepare_1 from " select v2, v from t1 left join t2 on v1 != v2 "`)
+	tk.MustQuery("execute prepare_1").Check(testkit.Rows("2 100", "1 <nil>"))
+	tk.MustQuery("execute prepare_1").Check(testkit.Rows("2 100", "1 <nil>"))
+}
+
 func (s *testPrepareSerialSuite) TestPrepareCacheForPartition(c *C) {
 	defer testleak.AfterTest(c)()
 	store, dom, err := newStoreWithBootstrap()