This is an automated email from the ASF dual-hosted git repository.

jihuayu pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new 34ce39843 feat(string): add digest length validation for DelEX 
IFDEQ/IFDNE (#3453)
34ce39843 is described below

commit 34ce39843bd3527d8e48e394b341fbb6c3c3a811
Author: kirito632 <[email protected]>
AuthorDate: Mon Apr 27 14:56:04 2026 +0800

    feat(string): add digest length validation for DelEX IFDEQ/IFDNE (#3453)
    
    - 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
    
    ### AI-Assisted Contribution Disclosure
    This contribution complies with the [ASF AI-assisted contribution
    
guidelines](https://kvrocks.apache.org/community/contributing#guidelines-for-ai-assisted-contributions).
    * **AI Tool Usage :** AI was used to help format the Go test template
    based on the reviewer's feedback and to reference the `util::EqualICase`
    pattern.
    * **Human Implementation & Validation:** I manually implemented the
    case-insensitive digest comparison logic for the `DelEX` command and
    integrated the requested test cases. I fully understand these behavioral
    changes (aligning with Redis Stack) and have successfully run all
    regression tests locally.
    
    This commit only modifies DelEX command behavior.
    
    ---------
    
    Co-authored-by: 纪华裕 <[email protected]>
---
 src/commands/cmd_string.cc                     | 12 +++++--
 src/types/redis_string.cc                      |  4 +--
 tests/gocase/unit/type/strings/strings_test.go | 47 ++++++++++++++++++++++++++
 3 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/src/commands/cmd_string.cc b/src/commands/cmd_string.cc
index d9ff39770..275846a2b 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/src/types/redis_string.cc b/src/types/redis_string.cc
index e056d3413..a99eaac3d 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 8f6648a51..204907a9f 100644
--- a/tests/gocase/unit/type/strings/strings_test.go
+++ b/tests/gocase/unit/type/strings/strings_test.go
@@ -349,6 +349,38 @@ 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"
+
+               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())
@@ -1204,4 +1236,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())
+       })
 }

Reply via email to