Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for get in set command 207 #1160

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions integration_tests/commands/resp/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,106 @@ func TestSet(t *testing.T) {
})
}
}
func TestSetWithGetOption(t *testing.T) {
conn := getLocalConnection()
expiryTime := strconv.FormatInt(time.Now().Add(1*time.Minute).UnixMilli(), 10) // For PXAT expiry
defer conn.Close()

testCases := []TestCase{
{
name: "Set with GET on Existing Key",
commands: []string{"SET k old_value", "SET k new_value GET", "GET k"},
expected: []interface{}{"OK", "old_value", "new_value"},
},
{
name: "Set with GET on Non-Existent Key",
commands: []string{"SET k new_value GET", "GET k"},
expected: []interface{}{"(nil)", "new_value"},
},
{
name: "Set with GET and NX on Non-Existent Key",
commands: []string{"DEL k", "SET k new_value NX GET", "GET k"},
expected: []interface{}{int64(0), "(nil)", "new_value"},
},
{
name: "Bitwise Set and Retrieve with GET",
commands: []string{"SETBIT k 1 1", "GET k", "SET k val GET"},
expected: []interface{}{int64(0), "@", "@"},
},
{
name: "Set with GET and NX on Existing Key",
commands: []string{"SET k old_value", "SET k new_value NX GET", "GET k"},
expected: []interface{}{"OK", "(nil)", "old_value"},
},
{
name: "Set with GET and XX on Existing Key",
commands: []string{"SET k old_value", "SET k new_value XX GET", "GET k"},
expected: []interface{}{"OK", "old_value", "new_value"},
},
{
name: "Set with GET and XX on Non-Existent Key",
commands: []string{"SET k new_value XX GET", "GET k"},
expected: []interface{}{"(nil)", "(nil)"},
},
{
name: "Set with GET and Expiry EX option",
commands: []string{"SET k old_value", "SET k new_value GET EX 2", "GET k", "SLEEP 3", "GET k"},
expected: []interface{}{"OK", "old_value", "new_value", "OK", "(nil)"},
},
{
name: "Set with GET and Expiry PX option",
commands: []string{"SET k old_value", "SET k new_value GET PX 2000", "GET k", "SLEEP 3", "GET k"},
expected: []interface{}{"OK", "old_value", "new_value", "OK", "(nil)"},
},
{
name: "Set with GET and Expiry PXAT option",
commands: []string{"SET k old_value", "SET k new_value GET PXAT " + expiryTime, "GET k"},
expected: []interface{}{"OK", "old_value", "new_value"},
},
{
name: "Set with GET and Invalid PXAT timestamp",
commands: []string{"SET k new_value GET PXAT 123123", "GET k"},
expected: []interface{}{"(nil)", "(nil)"},
},
{
name: "Set with GET and KEEPTTL",
commands: []string{"SET k old_value EX 5", "SET k new_value GET KEEPTTL", "GET k"},
expected: []interface{}{"OK", "old_value", "new_value"},
},
// Edge cases
{
name: "Set with GET and Both EX and PX options",
commands: []string{"SET k new_value GET EX 2 PX 2000"},
expected: []interface{}{"ERR syntax error"},
},
{
name: "Set with GET and NX on an Already Existing Key",
commands: []string{"DEL k", "SET k existing_value", "SET k new_value NX GET", "GET k"},
expected: []interface{}{int64(0), "OK", "(nil)", "existing_value"},
},
{
name: "Set with GET and XX on a Non-Existent Key",
commands: []string{"DEL k", "SET k new_value XX GET", "GET k"},
expected: []interface{}{int64(0), "(nil)", "(nil)"},
},
{
name: "Set with GET and NX followed by XX",
commands: []string{"SET k first_value NX GET", "SET k second_value XX GET", "GET k"},
expected: []interface{}{"(nil)", "first_value", "second_value"},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Clean up keys before each test
FireCommand(conn, "DEL k")
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
assert.Equal(t, tc.expected[i], result)
}
})
}
}

func TestSetWithOptions(t *testing.T) {
conn := getLocalConnection()
Expand Down
12 changes: 12 additions & 0 deletions internal/eval/store_eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func evalSET(args []string, store *dstore.Store) *EvalResponse {
var exDurationMs int64 = -1
var state exDurationState = Uninitialized
var keepttl bool = false
var isGetCmdPresent bool = false
var getResp *EvalResponse

key, value = args[0], args[1]
oType, oEnc := deduceTypeEncoding(value)
Expand Down Expand Up @@ -149,6 +151,13 @@ func evalSET(args []string, store *dstore.Store) *EvalResponse {
}
case KeepTTL:
keepttl = true
case GET:
isGetCmdPresent = true
getResp = evalGET([]string{key}, store)
// Abort SET operation if it has error
if getResp.Error != nil {
return getResp
}
default:
return &EvalResponse{
Result: nil,
Expand All @@ -174,6 +183,9 @@ func evalSET(args []string, store *dstore.Store) *EvalResponse {
// putting the k and value in a Hash Table
store.Put(key, store.NewObj(storedValue, exDurationMs, oType, oEnc), dstore.WithKeepTTL(keepttl))

if isGetCmdPresent {
return getResp
}
return &EvalResponse{
Result: clientio.OK,
Error: nil,
Expand Down