Skip to content

feat(string): support IFEQ/IFNE/IFDEQ/IFDNE in SET command (#3311)#3393

Open
kirito632 wants to merge 8 commits intoapache:unstablefrom
kirito632:feat/set-ifeq-ifne-ifdeq-ifdne
Open

feat(string): support IFEQ/IFNE/IFDEQ/IFDNE in SET command (#3311)#3393
kirito632 wants to merge 8 commits intoapache:unstablefrom
kirito632:feat/set-ifeq-ifne-ifdeq-ifdne

Conversation

@kirito632
Copy link
Copy Markdown

@kirito632 kirito632 commented Mar 18, 2026

What this PR does / why we need it

This PR implements the IFEQ, IFNE, IFDEQ, and IFDNE options for the
SET command, resolving #3311.

  • IFEQ <value>: only set if key exists and current value equals <value>
  • IFNE <value>: only set if key does not exist, or current value differs from <value>
  • IFDEQ <digest>: only set if key exists and digest of current value equals <digest>
  • IFDNE <digest>: only set if key does not exist, or digest of current value differs from <digest>

Which issue(s) this PR fixes

Closes #3311

Test coverage

  • Added 6 C++ unit tests covering all four options, plus GET and TTL combinations
  • Added Go integration tests in TestSetConditional covering syntax errors,
    basic behavior, GET/EX combinations, and 10 property-based tests

AI assistance

AI tools were used for the initial implementation draft and test scaffolding.
Logic was reviewed manually against the Redis 8.x documentation and kvrocks
coding conventions.

@PragmaTwice
Copy link
Copy Markdown
Member

@kirito632
Copy link
Copy Markdown
Author

Thanks for the pointer. I used AI tools to assist with the initial implementation draft and test scaffolding. I reviewed the logic myself against the Redis 8.x documentation, caught and fixed a wrong test assertion (GET+IFEQ condition-not-met should return the old value, not nil), and cleaned up non-ASCII comments to match kvrocks conventions. I understand the implementation and can explain any part of it.

@kirito632
Copy link
Copy Markdown
Author

Hi @PragmaTwice @jihuayu , I have addressed the review comments and fixed the clang-tidy / Go test issues in my latest commit . I also updated the branch to resolve the out-of-date issue.
PTAL, thanks! (Could you please help trigger the CI for me?)

@jihuayu
Copy link
Copy Markdown
Member

jihuayu commented Apr 13, 2026

Hi @kirito632. You need to ensure the CI passes before we start the review.

When GET is combined with IFEQ/IFNE/IFDEQ/IFDNE and the condition is
not met, return the previous value (not nil), consistent with how
Redis handles GET+NX/XX and the official Redis 8.x documentation.
When GET is combined with a conditional SET (IFEQ/IFNE/IFDEQ/IFDNE),
the previous value is always returned regardless of whether the
condition was met. The Go test incorrectly expected nil when the
condition was not met; fix it to expect the old value.

Also replace non-ASCII arrow characters in C++ test comments with
plain ASCII equivalents to match kvrocks coding conventions.
…dling

- Change StringSetArgs parameter to const reference in Set() declaration
  and definition to satisfy clang-tidy performance-unnecessary-value-param
- Fix Go tests: use .Val() instead of .Result() when nil response is
  expected, since go-redis treats nil as redis.Nil error in .Result()
- Fix missing-cmp_value error message check: actual error is
  'ERR no more item to parse', not a syntax error
- Return WRONGTYPE when IFEQ/IFNE/IFDEQ/IFDNE is used on non-string keys
- Treat NX with wrong type as "key exists" so the condition is not met
- Keep other behaviors unchanged
@kirito632 kirito632 force-pushed the feat/set-ifeq-ifne-ifdeq-ifdne branch from 7fdd1fe to d0343e5 Compare April 15, 2026 01:01
@kirito632
Copy link
Copy Markdown
Author

@jihuayu All CI checks pass locally now:

Lint & Format:

  • ./x.py check format - passed
  • ./x.py check tidy - passed

C++ Unit Tests:

  • ./x.py test cpp - 516 passed, 1 skipped (bitmap test requires USE_BITMAP flag)

Go Integration Tests:

  • go test ./tests/gocase/unit/type/strings/... - all passed (including TestSetConditional).
  • go test ./tests/gocase/integration/replication/... - all 20 test functions passed.

The WRONGTYPE handling fix is in the last commit. Ready for review.

@jihuayu
Copy link
Copy Markdown
Member

jihuayu commented Apr 15, 2026

@kirito632 Could you please tell me your clang-format and clang-tidy versions?

Copy link
Copy Markdown
Member

@jihuayu jihuayu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for you work! Looks good overall, just a few things to fix up:

  1. This PR is too large. We prefer one command per PR—please split this into 2–4 smaller PRs.
  2. Only add C++ tests if the Go tests can't cover the logic. Please double-check if these are necessary.
  3. Please add the following test cases for failure scenarios:
	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("Extended SET GET and NX option on wrong type", func(t *testing.T) {
		require.NoError(t, rdb.Del(ctx, "listkey").Err())
		require.NoError(t, rdb.LPush(ctx, "listkey", "v1").Err())

		require.ErrorContains(t, rdb.Do(ctx, "SET", "listkey", "v", "NX", "GET").Err(), "WRONGTYPE")
	})

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support of IFEQ/IFNE/IFDEQ/IFDNE in SET command

3 participants