Repository: pingcap/tidb (Go)

Issue:
`pre_split_regions` doesn't work with partition table
## Bug Report

Please answer these questions before submitting your issue. Thanks!

1. What did you do?
If possible, provide a recipe for reproducing the error.
CREATE TABLE partition_table (
    task_id BIGINT NOT NULL,
    creation_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    result_data JSON
)
shard_row_id_bits = 4 
pre_split_regions = 3
PARTITION BY RANGE (UNIX_TIMESTAMP(creation_dt) ) (
    PARTITION `20200101` VALUES LESS THAN (UNIX_TIMESTAMP('2020-01-01 00:00:00')),
    PARTITION `20200102` VALUES LESS THAN (UNIX_TIMESTAMP('2020-01-02 00:00:00')),
    PARTITION `20200103` VALUES LESS THAN (UNIX_TIMESTAMP('2020-01-03 00:00:00'))
);
SHOW TABLE partition_table REGIONS;

2. What did you expect to see?
I expect to see `number_of_partitions` * `2 ^ pre_split_regions` regions are created with the table creation. So in my test case, I expect to see 3 * 8 = 24 regions are created.

3. What did you see instead?
Only 3 regions are created, one for each partition.

4. What version of TiDB are you using (`tidb-server -V` or run `select tidb_version();` on TiDB)?
Release Version: v3.0.8↵Git Commit Hash: 8f13cf1449bd8903ff465a4f12ed89ecbac858a4↵Git Branch: HEAD↵UTC Build Time: 2019-12-31 11:14:59↵GoVersion: go version go1.13 linux/amd64↵Race Enabled: false↵TiKV Min Version: v3.0.0-60965b006877ca7234adaced7890d7b029ed1306↵Check Table Before Drop: false



Discussion:
@darkelf21cn Thanks for your report, We will fix this soon.

Currently, you can use `SPLIT TABLE` syntax to solve this problem. such as:

```sql
split table partition_table between (0) and (9223372036854775807) regions 8;

```
Hi @crazycs520 , I put this bugfix in milestone 3.0.10, please check if this is OK.



Patch:
diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go
index 564611621d68a..4a9ea76012427 100644
--- a/ddl/ddl_api.go
+++ b/ddl/ddl_api.go
@@ -1708,7 +1708,7 @@ func (d *ddl) preSplitAndScatter(ctx sessionctx.Context, tbInfo *model.TableInfo
 		scatterRegion = variable.TiDBOptOn(val)
 	}
 	if pi != nil {
-		preSplit = func() { splitPartitionTableRegion(ctx, sp, pi, scatterRegion) }
+		preSplit = func() { splitPartitionTableRegion(ctx, sp, tbInfo, pi, scatterRegion) }
 	} else {
 		preSplit = func() { splitTableRegion(ctx, sp, tbInfo, scatterRegion) }
 	}
diff --git a/ddl/split_region.go b/ddl/split_region.go
index 21cfd4298d322..ddc156b33edc2 100644
--- a/ddl/split_region.go
+++ b/ddl/split_region.go
@@ -24,13 +24,19 @@ import (
 	"go.uber.org/zap"
 )
 
-func splitPartitionTableRegion(ctx sessionctx.Context, store kv.SplittableStore, pi *model.PartitionInfo, scatter bool) {
+func splitPartitionTableRegion(ctx sessionctx.Context, store kv.SplittableStore, tbInfo *model.TableInfo, pi *model.PartitionInfo, scatter bool) {
 	// Max partition count is 4096, should we sample and just choose some of the partition to split?
 	regionIDs := make([]uint64, 0, len(pi.Definitions))
 	ctxWithTimeout, cancel := context.WithTimeout(context.Background(), ctx.GetSessionVars().GetSplitRegionTimeout())
 	defer cancel()
-	for _, def := range pi.Definitions {
-		regionIDs = append(regionIDs, splitRecordRegion(ctxWithTimeout, store, def.ID, scatter))
+	if tbInfo.ShardRowIDBits > 0 && tbInfo.PreSplitRegions > 0 {
+		for _, def := range pi.Definitions {
+			regionIDs = append(regionIDs, preSplitPhysicalTableByShardRowID(ctxWithTimeout, store, tbInfo, def.ID, scatter)...)
+		}
+	} else {
+		for _, def := range pi.Definitions {
+			regionIDs = append(regionIDs, splitRecordRegion(ctxWithTimeout, store, def.ID, scatter))
+		}
 	}
 	if scatter {
 		waitScatterRegionFinish(ctxWithTimeout, store, regionIDs...)
@@ -40,17 +46,18 @@ func splitPartitionTableRegion(ctx sessionctx.Context, store kv.SplittableStore,
 func splitTableRegion(ctx sessionctx.Context, store kv.SplittableStore, tbInfo *model.TableInfo, scatter bool) {
 	ctxWithTimeout, cancel := context.WithTimeout(context.Background(), ctx.GetSessionVars().GetSplitRegionTimeout())
 	defer cancel()
+	var regionIDs []uint64
 	if tbInfo.ShardRowIDBits > 0 && tbInfo.PreSplitRegions > 0 {
-		splitPreSplitedTable(ctxWithTimeout, store, tbInfo, scatter)
+		regionIDs = preSplitPhysicalTableByShardRowID(ctxWithTimeout, store, tbInfo, tbInfo.ID, scatter)
 	} else {
-		regionID := splitRecordRegion(ctxWithTimeout, store, tbInfo.ID, scatter)
-		if scatter {
-			waitScatterRegionFinish(ctxWithTimeout, store, regionID)
-		}
+		regionIDs = append(regionIDs, splitRecordRegion(ctxWithTimeout, store, tbInfo.ID, scatter))
+	}
+	if scatter {
+		waitScatterRegionFinish(ctxWithTimeout, store, regionIDs...)
 	}
 }
 
-func splitPreSplitedTable(ctx context.Context, store kv.SplittableStore, tbInfo *model.TableInfo, scatter bool) {
+func preSplitPhysicalTableByShardRowID(ctx context.Context, store kv.SplittableStore, tbInfo *model.TableInfo, physicalID int64, scatter bool) []uint64 {
 	// Example:
 	// ShardRowIDBits = 4
 	// PreSplitRegions = 2
@@ -78,9 +85,10 @@ func splitPreSplitedTable(ctx context.Context, store kv.SplittableStore, tbInfo
 	step := int64(1 << (tbInfo.ShardRowIDBits - tbInfo.PreSplitRegions))
 	max := int64(1 << tbInfo.ShardRowIDBits)
 	splitTableKeys := make([][]byte, 0, 1<<(tbInfo.PreSplitRegions))
+	splitTableKeys = append(splitTableKeys, tablecodec.GenTablePrefix(physicalID))
 	for p := step; p < max; p += step {
 		recordID := p << (64 - tbInfo.ShardRowIDBits - 1)
-		recordPrefix := tablecodec.GenTableRecordPrefix(tbInfo.ID)
+		recordPrefix := tablecodec.GenTableRecordPrefix(physicalID)
 		key := tablecodec.EncodeRecordKey(recordPrefix, kv.IntHandle(recordID))
 		splitTableKeys = append(splitTableKeys, key)
 	}
@@ -91,9 +99,7 @@ func splitPreSplitedTable(ctx context.Context, store kv.SplittableStore, tbInfo
 			zap.Stringer("table", tbInfo.Name), zap.Int("successful region count", len(regionIDs)), zap.Error(err))
 	}
 	regionIDs = append(regionIDs, splitIndexRegion(store, tbInfo, scatter)...)
-	if scatter {
-		waitScatterRegionFinish(ctx, store, regionIDs...)
-	}
+	return regionIDs
 }
 
 func splitRecordRegion(ctx context.Context, store kv.SplittableStore, tableID int64, scatter bool) uint64 {


Test patch:
diff --git a/executor/executor_test.go b/executor/executor_test.go
index 0c13e73a6197f..70289d5486f6b 100644
--- a/executor/executor_test.go
+++ b/executor/executor_test.go
@@ -4409,6 +4409,23 @@ func (s *testSplitTable) TestShowTableRegion(c *C) {
 	c.Assert(rows[1][1], Equals, fmt.Sprintf("t_%d_r_2305843009213693952", tbl.Meta().ID))
 	c.Assert(rows[2][1], Equals, fmt.Sprintf("t_%d_r_4611686018427387904", tbl.Meta().ID))
 	c.Assert(rows[3][1], Equals, fmt.Sprintf("t_%d_r_6917529027641081856", tbl.Meta().ID))
+
+	// Test pre-split table region when create table.
+	tk.MustExec("drop table if exists pt_pre")
+	tk.MustExec("create table pt_pre (a int, b int) shard_row_id_bits = 2 pre_split_regions=2 partition by hash(a) partitions 3;")
+	re = tk.MustQuery("show table pt_pre regions")
+	rows = re.Rows()
+	// Table t_regions should have 4 regions now.
+	c.Assert(len(rows), Equals, 12)
+	tbl = testGetTableByName(c, tk.Se, "test", "pt_pre")
+	pi := tbl.Meta().GetPartitionInfo().Definitions
+	c.Assert(len(pi), Equals, 3)
+	for i, p := range pi {
+		c.Assert(rows[1+4*i][1], Equals, fmt.Sprintf("t_%d_r_2305843009213693952", p.ID))
+		c.Assert(rows[2+4*i][1], Equals, fmt.Sprintf("t_%d_r_4611686018427387904", p.ID))
+		c.Assert(rows[3+4*i][1], Equals, fmt.Sprintf("t_%d_r_6917529027641081856", p.ID))
+	}
+
 	defer atomic.StoreUint32(&ddl.EnableSplitTableRegion, 0)
 
 	// Test split partition table.