diff --git a/pkg/ccl/logictestccl/testdata/logic_test/partitioning_hash_sharded_index b/pkg/ccl/logictestccl/testdata/logic_test/partitioning_hash_sharded_index index d5e419bbebc6..260f06ec5055 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/partitioning_hash_sharded_index +++ b/pkg/ccl/logictestccl/testdata/logic_test/partitioning_hash_sharded_index @@ -1380,3 +1380,46 @@ vectorized: true └── • scan buffer columns: (id, email, part, crdb_internal_email_shard_16, email_new, crdb_internal_email_shard_16_cast, check2) label: buffer 1 + +subtest test_presplit_with_partitioning + +statement ok +CREATE TABLE t_presplit ( + user_id INT PRIMARY KEY, + city STRING NOT NULL CHECK (city IN ('seattle', 'new york')), + member_id INT +) PARTITION ALL BY LIST (city) ( + PARTITION us_west VALUES IN (('seattle')), + PARTITION us_east VALUES IN (('new york')) +); + +statement ok +CREATE INDEX t_presplit_idx_member_id ON t_presplit (member_id) USING HASH WITH (bucket_count=8); + +skipif config 3node-tenant +query TITTT colnames,retry +SELECT t.name, r.table_id, r.index_name, r.start_pretty, r.end_pretty +FROM crdb_internal.tables t +JOIN crdb_internal.ranges r ON t.table_id = r.table_id +WHERE t.name = 't_presplit' +AND t.state = 'PUBLIC' +AND r.split_enforced_until IS NOT NULL; +---- +name table_id index_name start_pretty end_pretty +t_presplit 116 t_presplit_idx_member_id /Table/116/2 /Table/116/2/"new york"/0 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"new york"/0 /Table/116/2/"new york"/1 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"new york"/1 /Table/116/2/"new york"/2 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"new york"/2 /Table/116/2/"new york"/3 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"new york"/3 /Table/116/2/"new york"/4 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"new york"/4 /Table/116/2/"new york"/5 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"new york"/5 /Table/116/2/"new york"/6 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"new york"/6 /Table/116/2/"new york"/7 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"new york"/7 /Table/116/2/"seattle"/0 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"seattle"/0 /Table/116/2/"seattle"/1 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"seattle"/1 /Table/116/2/"seattle"/2 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"seattle"/2 /Table/116/2/"seattle"/3 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"seattle"/3 /Table/116/2/"seattle"/4 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"seattle"/4 /Table/116/2/"seattle"/5 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"seattle"/5 /Table/116/2/"seattle"/6 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"seattle"/6 /Table/116/2/"seattle"/7 +t_presplit 116 t_presplit_idx_member_id /Table/116/2/"seattle"/7 /Max diff --git a/pkg/sql/schema_changer.go b/pkg/sql/schema_changer.go index cd196ae1cbb8..a8447e135bd4 100644 --- a/pkg/sql/schema_changer.go +++ b/pkg/sql/schema_changer.go @@ -43,6 +43,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/parser" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" + "github.com/cockroachdb/cockroach/pkg/sql/rowenc" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" @@ -2655,14 +2656,49 @@ func (sc *SchemaChanger) preSplitHashShardedIndexRanges(ctx context.Context) err } if idx := m.AsIndex(); m.Adding() && m.DeleteOnly() && idx != nil { - // TODO (issue #76507): also pre-split partitioned hash sharded index. - if idx.IsSharded() && idx.GetPartitioning().PartitioningDesc().NumImplicitColumns == 0 { + if idx.IsSharded() { + var partitionKeyPrefixes []roachpb.Key + partitioning := idx.GetPartitioning() + partitioning.ForEachList( + func(name string, values [][]byte, subPartitioning catalog.Partitioning) error { + for _, tupleBytes := range values { + _, key, err := rowenc.DecodePartitionTuple( + &tree.DatumAlloc{}, + sc.execCfg.Codec, + tableDesc, + idx, + partitioning, + tupleBytes, + tree.Datums{}, + ) + if err != nil { + return nil + } + partitionKeyPrefixes = append(partitionKeyPrefixes, key) + } + return nil + }, + ) + splitAtShards := calculateSplitAtShards(maxHashShardedIndexRangePreSplit.Get(&sc.settings.SV), idx.GetSharded().ShardBuckets) - for _, shard := range splitAtShards { - keyPrefix := sc.execCfg.Codec.IndexPrefix(uint32(tableDesc.GetID()), uint32(idx.GetID())) - splitKey := encoding.EncodeVarintAscending(keyPrefix, shard) - if err := sc.db.SplitAndScatter(ctx, splitKey, hour); err != nil { - return err + if len(partitionKeyPrefixes) == 0 { + // If there is no partitioning on the index, only pre-split on + // selected shard boundaries. + for _, shard := range splitAtShards { + keyPrefix := sc.execCfg.Codec.IndexPrefix(uint32(tableDesc.GetID()), uint32(idx.GetID())) + splitKey := encoding.EncodeVarintAscending(keyPrefix, shard) + if err := sc.db.SplitAndScatter(ctx, splitKey, hour); err != nil { + return err + } + } + } else { + for _, partPrefix := range partitionKeyPrefixes { + for _, shard := range splitAtShards { + splitKey := encoding.EncodeVarintAscending(partPrefix, shard) + if err := sc.db.SplitAndScatter(ctx, splitKey, hour); err != nil { + return err + } + } } } }