diff --git a/src/types/redis_stream.cc b/src/types/redis_stream.cc index 60606df08a4..6dc47fba243 100644 --- a/src/types/redis_stream.cc +++ b/src/types/redis_stream.cc @@ -685,9 +685,6 @@ rocksdb::Status Stream::AutoClaim(engine::Context &ctx, const Slice &stream_name } rocksdb::Status Stream::CreateGroup(engine::Context &ctx, const Slice &stream_name, const StreamXGroupCreateOptions &options, const std::string &group_name) { - if (std::isdigit(group_name[0])) { - return rocksdb::Status::InvalidArgument("group name cannot start with number"); - } std::string ns_key = AppendNamespacePrefix(stream_name); StreamMetadata metadata; @@ -797,9 +794,6 @@ rocksdb::Status Stream::DestroyGroup(engine::Context &ctx, const Slice &stream_n rocksdb::Status Stream::createConsumerWithoutLock(engine::Context &ctx, const Slice &stream_name, const std::string &group_name, const std::string &consumer_name, int *created_number) { - if (std::isdigit(consumer_name[0])) { - return rocksdb::Status::InvalidArgument("consumer name cannot start with number"); - } std::string ns_key = AppendNamespacePrefix(stream_name); StreamMetadata metadata; rocksdb::Status s = GetMetadata(ctx, ns_key, &metadata); diff --git a/tests/gocase/unit/type/stream/stream_test.go b/tests/gocase/unit/type/stream/stream_test.go index df065ebf997..1590847afde 100644 --- a/tests/gocase/unit/type/stream/stream_test.go +++ b/tests/gocase/unit/type/stream/stream_test.go @@ -962,7 +962,8 @@ func TestStreamOffset(t *testing.T) { require.Error(t, rdb.Do(ctx, "XGROUP", "CREAT", streamName, groupName, "$").Err()) require.Error(t, rdb.Do(ctx, "XGROUP", "CREATE", streamName, groupName, "$", "ENTRIEREAD", "10").Err()) require.Error(t, rdb.Do(ctx, "XGROUP", "CREATE", streamName, groupName, "$", "ENTRIESREAD", "-10").Err()) - require.Error(t, rdb.Do(ctx, "XGROUP", "CREATE", streamName, "1test-group-c", "$").Err()) + // Group names may start with a digit (RESP bulk string); same as Redis — not a syntax error. + require.NoError(t, rdb.Do(ctx, "XGROUP", "CREATE", streamName, "1test-group-c", "$").Err()) require.NoError(t, rdb.Del(ctx, "myStream").Err()) require.NoError(t, rdb.XAdd(ctx, &redis.XAddArgs{Stream: "myStream", Values: []string{"iTeM", "1", "vAluE", "a"}}).Err()) @@ -978,6 +979,30 @@ func TestStreamOffset(t *testing.T) { require.Equal(t, int64(0), result) }) + t.Run("XGROUP CREATE and CREATECONSUMER with names starting with digits", func(t *testing.T) { + streamKey := "stream-digit-names" + require.NoError(t, rdb.Del(ctx, streamKey).Err()) + require.NoError(t, rdb.XAdd(ctx, &redis.XAddArgs{Stream: streamKey, Values: []string{"f", "v"}}).Err()) + + // Group name starting with a digit should be allowed (matches Redis behavior) + require.NoError(t, rdb.XGroupCreateMkStream(ctx, streamKey, "1group", "0").Err()) + + // Consumer name starting with a digit should be allowed + result := rdb.Do(ctx, "XGROUP", "CREATECONSUMER", streamKey, "1group", "2consumer") + require.NoError(t, result.Err()) + + // Read with the digit-prefixed group and consumer + _, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{ + Group: "1group", + Consumer: "2consumer", + Streams: []string{streamKey, ">"}, + Count: 10, + }).Result() + require.NoError(t, err) + + require.NoError(t, rdb.Del(ctx, streamKey).Err()) + }) + t.Run("XGROUP CREATECONSUMER with different kinds of commands", func(t *testing.T) { streamName := "test-stream" groupName := "test-group"