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())
+ })
}