Repository: pingcap/tidb (Go)

Issue:
BatchPointGet returns wrong results with condition `in (null)` in some cases
## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)

<!-- a step by step guide for reproducing the bug. -->
```
create table t ( id bigint(10) primary key, f varchar(191) default null, unique key `idx_f` (`f`));
insert into t values (1, "");
select * from t where f in (null);
```

### 2. What did you expect to see? (Required)
Return an empty set.

### 3. What did you see instead (Required)
```
mysql> select * from t where f in (null);
+----+------+
| id | f    |
+----+------+
|  1 |      |
+----+------+
1 row in set (0.00 sec)
```

### 4. Affected version (Required)

<!-- v3.0.0, v4.0.0, etc -->
Master branch:
```
mysql> select version();
+------------------------------------------+
| version()                                |
+------------------------------------------+
| 5.7.25-TiDB-v4.0.0-beta.2-849-gac581ee01 |
+------------------------------------------+
1 row in set (0.00 sec)
```

### 5. Root Cause Analysis

<!-- should be filled by the investigator before it's closed -->

`Datum.ToString()` returns "" if its type is `KindNull`, which conflicts with empty strings.



Discussion:
This problem seems introduced by this PR(https://github.com/pingcap/tidb/pull/17894), PTAL @tiancaiamao 
@Hexilee PTAL



Patch:
diff --git a/executor/batch_point_get.go b/executor/batch_point_get.go
index dc2d8df845da4..9023eea5c1f7c 100644
--- a/executor/batch_point_get.go
+++ b/executor/batch_point_get.go
@@ -160,6 +160,15 @@ func (e *BatchPointGetExec) Next(ctx context.Context, req *chunk.Chunk) error {
 	return nil
 }
 
+func datumsContainNull(vals []types.Datum) bool {
+	for _, val := range vals {
+		if val.IsNull() {
+			return true
+		}
+	}
+	return false
+}
+
 func (e *BatchPointGetExec) initialize(ctx context.Context) error {
 	var handleVals map[string][]byte
 	var indexKeys []kv.Key
@@ -170,6 +179,11 @@ func (e *BatchPointGetExec) initialize(ctx context.Context) error {
 		dedup := make(map[hack.MutableString]struct{})
 		keys := make([]kv.Key, 0, len(e.idxVals))
 		for _, idxVals := range e.idxVals {
+			// For all x, 'x IN (null)' evaluate to null, so the query get no result.
+			if datumsContainNull(idxVals) {
+				continue
+			}
+
 			physID := getPhysID(e.tblInfo, idxVals[e.partPos].GetInt64())
 			idxKey, err1 := EncodeUniqueIndexKey(e.ctx, e.tblInfo, e.idxInfo, idxVals, physID)
 			if err1 != nil && !kv.ErrNotExist.Equal(err1) {
@@ -192,6 +206,11 @@ func (e *BatchPointGetExec) initialize(ctx context.Context) error {
 		}
 		indexKeys = keys
 
+		// SELECT * FROM t WHERE x IN (null), in this case there is no key.
+		if len(keys) == 0 {
+			return nil
+		}
+
 		// Fetch all handles.
 		handleVals, err = batchGetter.BatchGet(ctx, keys)
 		if err != nil {


Test patch:
diff --git a/executor/batch_point_get_test.go b/executor/batch_point_get_test.go
index 4429c400ee8d2..fc2d30538db2f 100644
--- a/executor/batch_point_get_test.go
+++ b/executor/batch_point_get_test.go
@@ -138,3 +138,15 @@ func (s *testBatchPointGetSuite) TestBatchPointGetCache(c *C) {
 	tk.MustQuery("SELECT id, token FROM test.customers WHERE id IN (28)")
 	tk.MustQuery("SELECT id, token FROM test.customers WHERE id IN (28, 29);").Check(testkit.Rows("28 07j", "29 03j"))
 }
+
+func (s *testBatchPointGetSuite) TestIssue18843(c *C) {
+	tk := testkit.NewTestKit(c, s.store)
+	tk.MustExec("use test")
+	tk.MustExec("create table t18843 ( id bigint(10) primary key, f varchar(191) default null, unique key `idx_f` (`f`))")
+	tk.MustExec("insert into t18843 values (1, '')")
+	tk.MustQuery("select * from t18843 where f in (null)").Check(testkit.Rows())
+
+	tk.MustExec("insert into t18843 values (2, null)")
+	tk.MustQuery("select * from t18843 where f in (null)").Check(testkit.Rows())
+	tk.MustQuery("select * from t18843 where f is null").Check(testkit.Rows("2 <nil>"))
+}