From 3cdb2333ac662589db60c96247f341d58d0453ec Mon Sep 17 00:00:00 2001 From: kirito632 Date: Wed, 15 Apr 2026 18:48:08 +0800 Subject: [PATCH 1/3] feat(string): add digest length validation for DelEX IFDEQ/IFDNE - Add 16-character digest validation for IFDEQ/IFDNE options in DelEX command - Return clear error if digest length is not exactly 16 hexadecimal characters - Add test case for invalid digest length rejection as requested by reviewer This commit only modifies DelEX command behavior. --- src/commands/cmd_string.cc | 12 ++++++++++-- tests/gocase/unit/type/strings/strings_test.go | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/commands/cmd_string.cc b/src/commands/cmd_string.cc index d9ff39770de..275846a2ba1 100644 --- a/src/commands/cmd_string.cc +++ b/src/commands/cmd_string.cc @@ -118,9 +118,17 @@ class CommandDelEX : public Commander { CommandParser parser(args, 2); while (parser.Good()) { if (parser.EatEqICase("ifdeq")) { - option_ = {DelExOption::IFDEQ, GET_OR_RET(parser.TakeStr())}; + std::string digest = GET_OR_RET(parser.TakeStr()); + if (digest.size() != 16) { + return {Status::RedisParseErr, "ERR digest must be exactly 16 hexadecimal characters"}; + } + option_ = {DelExOption::IFDEQ, std::move(digest)}; } else if (parser.EatEqICase("ifdne")) { - option_ = {DelExOption::IFDNE, GET_OR_RET(parser.TakeStr())}; + std::string digest = GET_OR_RET(parser.TakeStr()); + if (digest.size() != 16) { + return {Status::RedisParseErr, "ERR digest must be exactly 16 hexadecimal characters"}; + } + option_ = {DelExOption::IFDNE, std::move(digest)}; } else if (parser.EatEqICase("ifeq")) { option_ = {DelExOption::IFEQ, GET_OR_RET(parser.TakeStr())}; } else if (parser.EatEqICase("ifne")) { diff --git a/tests/gocase/unit/type/strings/strings_test.go b/tests/gocase/unit/type/strings/strings_test.go index 8f6648a51ef..33f17e0c3b9 100644 --- a/tests/gocase/unit/type/strings/strings_test.go +++ b/tests/gocase/unit/type/strings/strings_test.go @@ -349,6 +349,21 @@ func testString(t *testing.T, configs util.KvrocksServerConfigs) { require.Equal(t, "", rdb.Get(ctx, value).Val()) }) + t.Run("DelEX IFDEQ and IFDNE reject invalid digest length", func(t *testing.T) { + key := "test-string-key-invalid-digest" + value := "Hello world" + + require.NoError(t, rdb.Del(ctx, key).Err()) + require.NoError(t, rdb.Set(ctx, key, value, 0).Err()) + require.ErrorContains(t, rdb.Do(ctx, "DelEX", key, "ifdeq", "123456789012345").Err(), + "exactly 16 hexadecimal characters") + require.Equal(t, value, rdb.Get(ctx, key).Val()) + + require.ErrorContains(t, rdb.Do(ctx, "DelEX", key, "ifdne", "123456789012345").Err(), + "exactly 16 hexadecimal characters") + require.Equal(t, value, rdb.Get(ctx, key).Val()) + }) + t.Run("MGET command", func(t *testing.T) { require.NoError(t, rdb.FlushDB(ctx).Err()) require.NoError(t, rdb.Set(ctx, "foo", "BAR", 0).Err()) From e9c7b8aac766c30e0cd74ec933095369a099c46b Mon Sep 17 00:00:00 2001 From: kirito632 Date: Thu, 16 Apr 2026 21:37:53 +0800 Subject: [PATCH 2/3] feat(string): add digest length validation for DelEX --- src/commands/cmd_string.cc | 12 ++++++++++-- tests/gocase/unit/type/strings/strings_test.go | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/commands/cmd_string.cc b/src/commands/cmd_string.cc index d9ff39770de..275846a2ba1 100644 --- a/src/commands/cmd_string.cc +++ b/src/commands/cmd_string.cc @@ -118,9 +118,17 @@ class CommandDelEX : public Commander { CommandParser parser(args, 2); while (parser.Good()) { if (parser.EatEqICase("ifdeq")) { - option_ = {DelExOption::IFDEQ, GET_OR_RET(parser.TakeStr())}; + std::string digest = GET_OR_RET(parser.TakeStr()); + if (digest.size() != 16) { + return {Status::RedisParseErr, "ERR digest must be exactly 16 hexadecimal characters"}; + } + option_ = {DelExOption::IFDEQ, std::move(digest)}; } else if (parser.EatEqICase("ifdne")) { - option_ = {DelExOption::IFDNE, GET_OR_RET(parser.TakeStr())}; + std::string digest = GET_OR_RET(parser.TakeStr()); + if (digest.size() != 16) { + return {Status::RedisParseErr, "ERR digest must be exactly 16 hexadecimal characters"}; + } + option_ = {DelExOption::IFDNE, std::move(digest)}; } else if (parser.EatEqICase("ifeq")) { option_ = {DelExOption::IFEQ, GET_OR_RET(parser.TakeStr())}; } else if (parser.EatEqICase("ifne")) { diff --git a/tests/gocase/unit/type/strings/strings_test.go b/tests/gocase/unit/type/strings/strings_test.go index 8f6648a51ef..4b30b1f47ac 100644 --- a/tests/gocase/unit/type/strings/strings_test.go +++ b/tests/gocase/unit/type/strings/strings_test.go @@ -1204,4 +1204,19 @@ func testString(t *testing.T, configs util.KvrocksServerConfigs) { require.Equal(t, []redis.LCSMatchedPosition{}, rdb.LCS(ctx, &redis.LCSQuery{Key1: "virus1", Key2: "virus2", Idx: true}).Val().Matches) require.Equal(t, []redis.LCSMatchedPosition{}, rdb.LCS(ctx, &redis.LCSQuery{Key1: "virus1", Key2: "virus2", Idx: true, WithMatchLen: true}).Val().Matches) }) + + t.Run("DelEX IFDEQ and IFDNE reject invalid digest length", func(t *testing.T) { + key := "test-string-key-invalid-digest" + value := "Hello world" + + require.NoError(t, rdb.Del(ctx, key).Err()) + require.NoError(t, rdb.Set(ctx, key, value, 0).Err()) + require.ErrorContains(t, rdb.Do(ctx, "DelEX", key, "ifdeq", "123456789012345").Err(), + "exactly 16 hexadecimal characters") + require.Equal(t, value, rdb.Get(ctx, key).Val()) + + require.ErrorContains(t, rdb.Do(ctx, "DelEX", key, "ifdne", "123456789012345").Err(), + "exactly 16 hexadecimal characters") + require.Equal(t, value, rdb.Get(ctx, key).Val()) + }) } From 8c18481ef183c8c7a72f08f393c5dccc03438dbe Mon Sep 17 00:00:00 2001 From: kirito632 Date: Fri, 17 Apr 2026 16:46:59 +0800 Subject: [PATCH 3/3] fix(string): support uppercase digest comparison in DelEX and add tests --- src/types/redis_string.cc | 4 ++-- tests/gocase/unit/type/strings/strings_test.go | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/types/redis_string.cc b/src/types/redis_string.cc index e056d341355..a99eaac3d3a 100644 --- a/src/types/redis_string.cc +++ b/src/types/redis_string.cc @@ -197,10 +197,10 @@ rocksdb::Status String::DelEX(engine::Context &ctx, const std::string &user_key, matched = true; break; case DelExOption::IFDEQ: - matched = option.value == util::StringDigest(val); + matched = util::EqualICase(option.value, util::StringDigest(val)); break; case DelExOption::IFDNE: - matched = option.value != util::StringDigest(val); + matched = !util::EqualICase(option.value, util::StringDigest(val)); break; case DelExOption::IFEQ: matched = option.value == val; diff --git a/tests/gocase/unit/type/strings/strings_test.go b/tests/gocase/unit/type/strings/strings_test.go index a93e74b12f7..204907a9ff3 100644 --- a/tests/gocase/unit/type/strings/strings_test.go +++ b/tests/gocase/unit/type/strings/strings_test.go @@ -349,6 +349,23 @@ func testString(t *testing.T, configs util.KvrocksServerConfigs) { require.Equal(t, "", rdb.Get(ctx, value).Val()) }) + t.Run("DelEX IFDEQ and IFDNE accept uppercase digest", func(t *testing.T) { + key := "test-string-key-uppercase-digest" + value := "Hello world" + var digest string + + require.NoError(t, rdb.Del(ctx, key).Err()) + require.NoError(t, rdb.Set(ctx, key, value, 0).Err()) + digest = strings.ToUpper(rdb.Do(ctx, "DIGEST", key).Val().(string)) + require.Equal(t, int64(1), rdb.Do(ctx, "DelEX", key, "ifdeq", digest).Val()) + require.Equal(t, int64(0), rdb.Exists(ctx, key).Val()) + + require.NoError(t, rdb.Set(ctx, key, value, 0).Err()) + digest = strings.ToUpper(rdb.Do(ctx, "DIGEST", key).Val().(string)) + require.Equal(t, int64(0), rdb.Do(ctx, "DelEX", key, "ifdne", digest).Val()) + require.Equal(t, value, rdb.Get(ctx, key).Val()) + }) + t.Run("DelEX IFDEQ and IFDNE reject invalid digest length", func(t *testing.T) { key := "test-string-key-invalid-digest" value := "Hello world"