Skip to content

Commit 4fe5edf

Browse files
sarthakaggarwal97vudiep411
authored andcommitted
Set Command with IFEQ Support (valkey-io#1324)
This PR allows the Valkey users to perform conditional updates where the SET command is completed if the given comparison-value matches the key’s current value. Syntax: ``` SET key value IFEQ comparison-value ``` Behavior: If the values match, the SET completes as expected. If they do not match, the command returns a (nil), except if the GET argument is also given (see below). Behavior with Additional Flags: 1. ```SET key value IFEQ comparison-value GET``` returns the existing value, regardless of whether it matches comparison-value or not. The conditional set operation is performed if the given comparison value matches the existing value. To check if the SET succeeded, the caller needs to check if the returned string matches the comparison-value. 2. ```SET key value IFEQ comparison-value XX``` is a syntax error. 3. ```SET key value IFEQ comparison-value NX``` is a syntax error. Closes: valkey-io#1215 --------- Signed-off-by: Sarthak Aggarwal <[email protected]>
1 parent 38c1603 commit 4fe5edf

File tree

5 files changed

+145
-41
lines changed

5 files changed

+145
-41
lines changed

src/commands.def

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10632,6 +10632,7 @@ commandHistory SET_History[] = {
1063210632
{"6.0.0","Added the `KEEPTTL` option."},
1063310633
{"6.2.0","Added the `GET`, `EXAT` and `PXAT` option."},
1063410634
{"7.0.0","Allowed the `NX` and `GET` options to be used together."},
10635+
{"8.1.0","Added the `IFEQ` option."},
1063510636
};
1063610637
#endif
1063710638

@@ -10649,8 +10650,9 @@ keySpec SET_Keyspecs[1] = {
1064910650

1065010651
/* SET condition argument table */
1065110652
struct COMMAND_ARG SET_condition_Subargs[] = {
10652-
{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
10653-
{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
10653+
{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,"2.6.12",CMD_ARG_NONE,0,NULL)},
10654+
{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,"2.6.12",CMD_ARG_NONE,0,NULL)},
10655+
{MAKE_ARG("comparison-value",ARG_TYPE_STRING,-1,"IFEQ","Sets the key's value only if the current value matches the specified comparison value.","8.1.0",CMD_ARG_NONE,0,NULL)},
1065410656
};
1065510657

1065610658
/* SET expiration argument table */
@@ -10666,7 +10668,7 @@ struct COMMAND_ARG SET_expiration_Subargs[] = {
1066610668
struct COMMAND_ARG SET_Args[] = {
1066710669
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
1066810670
{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
10669-
{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"2.6.12",CMD_ARG_OPTIONAL,2,NULL),.subargs=SET_condition_Subargs},
10671+
{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=SET_condition_Subargs},
1067010672
{MAKE_ARG("get",ARG_TYPE_PURE_TOKEN,-1,"GET",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
1067110673
{MAKE_ARG("expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=SET_expiration_Subargs},
1067210674
};
@@ -11139,7 +11141,7 @@ struct COMMAND_STRUCT serverCommandTable[] = {
1113911141
{MAKE_CMD("mset","Atomically creates or modifies the string values of one or more keys.","O(N) where N is the number of keys to set.","1.0.1",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,MSET_History,0,MSET_Tips,2,msetCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,MSET_Keyspecs,1,NULL,1),.args=MSET_Args},
1114011142
{MAKE_CMD("msetnx","Atomically modifies the string values of one or more keys only when all keys don't exist.","O(N) where N is the number of keys to set.","1.0.1",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,MSETNX_History,0,MSETNX_Tips,0,msetnxCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,MSETNX_Keyspecs,1,NULL,1),.args=MSETNX_Args},
1114111143
{MAKE_CMD("psetex","Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.","O(1)","2.6.0",CMD_DOC_DEPRECATED,"`SET` with the `PX` argument","2.6.12","string",COMMAND_GROUP_STRING,PSETEX_History,0,PSETEX_Tips,0,psetexCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,PSETEX_Keyspecs,1,NULL,3),.args=PSETEX_Args},
11142-
{MAKE_CMD("set","Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,SET_History,4,SET_Tips,0,setCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SET_Keyspecs,1,setGetKeys,5),.args=SET_Args},
11144+
{MAKE_CMD("set","Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,SET_History,5,SET_Tips,0,setCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SET_Keyspecs,1,setGetKeys,5),.args=SET_Args},
1114311145
{MAKE_CMD("setex","Sets the string value and expiration time of a key. Creates the key if it doesn't exist.","O(1)","2.0.0",CMD_DOC_DEPRECATED,"`SET` with the `EX` argument","2.6.12","string",COMMAND_GROUP_STRING,SETEX_History,0,SETEX_Tips,0,setexCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SETEX_Keyspecs,1,NULL,3),.args=SETEX_Args},
1114411146
{MAKE_CMD("setnx","Set the string value of a key only when the key doesn't exist.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`SET` with the `NX` argument","2.6.12","string",COMMAND_GROUP_STRING,SETNX_History,0,SETNX_Tips,0,setnxCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,SETNX_Keyspecs,1,NULL,2),.args=SETNX_Args},
1114511147
{MAKE_CMD("setrange","Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.","O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.","2.2.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,SETRANGE_History,0,SETRANGE_Tips,0,setrangeCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SETRANGE_Keyspecs,1,NULL,3),.args=SETRANGE_Args},

src/commands/set.json

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
[
2424
"7.0.0",
2525
"Allowed the `NX` and `GET` options to be used together."
26+
],
27+
[
28+
"8.1.0",
29+
"Added the `IFEQ` option."
2630
]
2731
],
2832
"command_flags": [
@@ -89,17 +93,32 @@
8993
"name": "condition",
9094
"type": "oneof",
9195
"optional": true,
92-
"since": "2.6.12",
9396
"arguments": [
9497
{
9598
"name": "nx",
9699
"type": "pure-token",
97-
"token": "NX"
100+
"token": "NX",
101+
"since": "2.6.12"
98102
},
99103
{
100104
"name": "xx",
101105
"type": "pure-token",
102-
"token": "XX"
106+
"token": "XX",
107+
"since": "2.6.12"
108+
},
109+
{
110+
"name": "comparison-value",
111+
"type": "string",
112+
"token": "IFEQ",
113+
"since": "8.1.0",
114+
"summary": "Sets the key's value only if the current value matches the specified comparison value.",
115+
"arguments": [
116+
{
117+
"name": "comparison-value",
118+
"type": "string",
119+
"summary": "The value to compare with the current key's value before setting."
120+
}
121+
]
103122
}
104123
]
105124
},

src/t_string.c

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,16 @@ static int checkStringLength(client *c, long long size, long long append) {
6767
* If abort_reply is NULL, "$-1" is used. */
6868

6969
#define OBJ_NO_FLAGS 0
70-
#define OBJ_SET_NX (1 << 0) /* Set if key not exists. */
71-
#define OBJ_SET_XX (1 << 1) /* Set if key exists. */
72-
#define OBJ_EX (1 << 2) /* Set if time in seconds is given */
73-
#define OBJ_PX (1 << 3) /* Set if time in ms in given */
74-
#define OBJ_KEEPTTL (1 << 4) /* Set and keep the ttl */
75-
#define OBJ_SET_GET (1 << 5) /* Set if want to get key before set */
76-
#define OBJ_EXAT (1 << 6) /* Set if timestamp in second is given */
77-
#define OBJ_PXAT (1 << 7) /* Set if timestamp in ms is given */
78-
#define OBJ_PERSIST (1 << 8) /* Set if we need to remove the ttl */
70+
#define OBJ_SET_NX (1 << 0) /* Set if key not exists. */
71+
#define OBJ_SET_XX (1 << 1) /* Set if key exists. */
72+
#define OBJ_EX (1 << 2) /* Set if time in seconds is given */
73+
#define OBJ_PX (1 << 3) /* Set if time in ms in given */
74+
#define OBJ_KEEPTTL (1 << 4) /* Set and keep the ttl */
75+
#define OBJ_SET_GET (1 << 5) /* Set if want to get key before set */
76+
#define OBJ_EXAT (1 << 6) /* Set if timestamp in second is given */
77+
#define OBJ_PXAT (1 << 7) /* Set if timestamp in ms is given */
78+
#define OBJ_PERSIST (1 << 8) /* Set if we need to remove the ttl */
79+
#define OBJ_SET_IFEQ (1 << 9) /* Set if we need compare and set */
7980

8081
/* Forward declaration */
8182
static int getExpireMillisecondsOrReply(client *c, robj *expire, int flags, int unit, long long *milliseconds);
@@ -87,7 +88,8 @@ void setGenericCommand(client *c,
8788
robj *expire,
8889
int unit,
8990
robj *ok_reply,
90-
robj *abort_reply) {
91+
robj *abort_reply,
92+
robj *comparison) {
9193
long long milliseconds = 0; /* initialized to avoid any harmness warning */
9294
int found = 0;
9395
int setkey_flags = 0;
@@ -100,7 +102,27 @@ void setGenericCommand(client *c,
100102
if (getGenericCommand(c) == C_ERR) return;
101103
}
102104

103-
found = (lookupKeyWrite(c->db, key) != NULL);
105+
robj *existing_value = lookupKeyWrite(c->db, key);
106+
found = existing_value != NULL;
107+
108+
/* Handle the IFEQ conditional check */
109+
if (flags & OBJ_SET_IFEQ && found) {
110+
if (!(flags & OBJ_SET_GET) && checkType(c, existing_value, OBJ_STRING)) {
111+
return;
112+
}
113+
114+
if (compareStringObjects(existing_value, comparison) != 0) {
115+
if (!(flags & OBJ_SET_GET)) {
116+
addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
117+
}
118+
return;
119+
}
120+
} else if (flags & OBJ_SET_IFEQ && !found) {
121+
if (!(flags & OBJ_SET_GET)) {
122+
addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
123+
}
124+
return;
125+
}
104126

105127
if ((flags & OBJ_SET_NX && found) || (flags & OBJ_SET_XX && !found)) {
106128
if (!(flags & OBJ_SET_GET)) {
@@ -208,7 +230,7 @@ static int getExpireMillisecondsOrReply(client *c, robj *expire, int flags, int
208230
* string arguments used in SET and GET command.
209231
*
210232
* Get specific commands - PERSIST/DEL
211-
* Set specific commands - XX/NX/GET
233+
* Set specific commands - XX/NX/GET/IFEQ
212234
* Common commands - EX/EXAT/PX/PXAT/KEEPTTL
213235
*
214236
* Function takes pointers to client, flags, unit, pointer to pointer of expire obj if needed
@@ -219,7 +241,7 @@ static int getExpireMillisecondsOrReply(client *c, robj *expire, int flags, int
219241
* Input flags are updated upon parsing the arguments. Unit and expire are updated if there are any
220242
* EX/EXAT/PX/PXAT arguments. Unit is updated to millisecond if PX/PXAT is set.
221243
*/
222-
int parseExtendedStringArgumentsOrReply(client *c, int *flags, int *unit, robj **expire, int command_type) {
244+
int parseExtendedStringArgumentsOrReply(client *c, int *flags, int *unit, robj **expire, robj **compare_val, int command_type) {
223245
int j = command_type == COMMAND_GET ? 2 : 3;
224246
for (; j < c->argc; j++) {
225247
char *opt = c->argv[j]->ptr;
@@ -228,14 +250,23 @@ int parseExtendedStringArgumentsOrReply(client *c, int *flags, int *unit, robj *
228250
/* clang-format off */
229251
if ((opt[0] == 'n' || opt[0] == 'N') &&
230252
(opt[1] == 'x' || opt[1] == 'X') && opt[2] == '\0' &&
231-
!(*flags & OBJ_SET_XX) && (command_type == COMMAND_SET))
253+
!(*flags & OBJ_SET_XX || *flags & OBJ_SET_IFEQ) && (command_type == COMMAND_SET))
232254
{
233255
*flags |= OBJ_SET_NX;
234256
} else if ((opt[0] == 'x' || opt[0] == 'X') &&
235257
(opt[1] == 'x' || opt[1] == 'X') && opt[2] == '\0' &&
236-
!(*flags & OBJ_SET_NX) && (command_type == COMMAND_SET))
258+
!(*flags & OBJ_SET_NX || *flags & OBJ_SET_IFEQ) && (command_type == COMMAND_SET))
237259
{
238260
*flags |= OBJ_SET_XX;
261+
} else if ((opt[0] == 'i' || opt[0] == 'I') &&
262+
(opt[1] == 'f' || opt[1] == 'F') &&
263+
(opt[2] == 'e' || opt[2] == 'E') &&
264+
(opt[3] == 'q' || opt[3] == 'Q') && opt[4] == '\0' &&
265+
next && !(*flags & OBJ_SET_NX || *flags & OBJ_SET_XX || *flags & OBJ_SET_IFEQ) && (command_type == COMMAND_SET))
266+
{
267+
*flags |= OBJ_SET_IFEQ;
268+
*compare_val = next;
269+
j++;
239270
} else if ((opt[0] == 'g' || opt[0] == 'G') &&
240271
(opt[1] == 'e' || opt[1] == 'E') &&
241272
(opt[2] == 't' || opt[2] == 'T') && opt[3] == '\0' &&
@@ -304,34 +335,36 @@ int parseExtendedStringArgumentsOrReply(client *c, int *flags, int *unit, robj *
304335
return C_OK;
305336
}
306337

307-
/* SET key value [NX] [XX] [KEEPTTL] [GET] [EX <seconds>] [PX <milliseconds>]
308-
* [EXAT <seconds-timestamp>][PXAT <milliseconds-timestamp>] */
338+
/* SET key value [NX | XX | IFEQ comparison-value] [GET]
339+
* [EX seconds | PX milliseconds |
340+
* EXAT seconds-timestamp | PXAT milliseconds-timestamp | KEEPTTL] */
309341
void setCommand(client *c) {
310342
robj *expire = NULL;
343+
robj *comparison = NULL;
311344
int unit = UNIT_SECONDS;
312345
int flags = OBJ_NO_FLAGS;
313346

314-
if (parseExtendedStringArgumentsOrReply(c, &flags, &unit, &expire, COMMAND_SET) != C_OK) {
347+
if (parseExtendedStringArgumentsOrReply(c, &flags, &unit, &expire, &comparison, COMMAND_SET) != C_OK) {
315348
return;
316349
}
317350

318351
c->argv[2] = tryObjectEncoding(c->argv[2]);
319-
setGenericCommand(c, flags, c->argv[1], c->argv[2], expire, unit, NULL, NULL);
352+
setGenericCommand(c, flags, c->argv[1], c->argv[2], expire, unit, NULL, NULL, comparison);
320353
}
321354

322355
void setnxCommand(client *c) {
323356
c->argv[2] = tryObjectEncoding(c->argv[2]);
324-
setGenericCommand(c, OBJ_SET_NX, c->argv[1], c->argv[2], NULL, 0, shared.cone, shared.czero);
357+
setGenericCommand(c, OBJ_SET_NX, c->argv[1], c->argv[2], NULL, 0, shared.cone, shared.czero, NULL);
325358
}
326359

327360
void setexCommand(client *c) {
328361
c->argv[3] = tryObjectEncoding(c->argv[3]);
329-
setGenericCommand(c, OBJ_EX, c->argv[1], c->argv[3], c->argv[2], UNIT_SECONDS, NULL, NULL);
362+
setGenericCommand(c, OBJ_EX, c->argv[1], c->argv[3], c->argv[2], UNIT_SECONDS, NULL, NULL, NULL);
330363
}
331364

332365
void psetexCommand(client *c) {
333366
c->argv[3] = tryObjectEncoding(c->argv[3]);
334-
setGenericCommand(c, OBJ_PX, c->argv[1], c->argv[3], c->argv[2], UNIT_MILLISECONDS, NULL, NULL);
367+
setGenericCommand(c, OBJ_PX, c->argv[1], c->argv[3], c->argv[2], UNIT_MILLISECONDS, NULL, NULL, NULL);
335368
}
336369

337370
int getGenericCommand(client *c) {
@@ -377,7 +410,7 @@ void getexCommand(client *c) {
377410
int unit = UNIT_SECONDS;
378411
int flags = OBJ_NO_FLAGS;
379412

380-
if (parseExtendedStringArgumentsOrReply(c, &flags, &unit, &expire, COMMAND_GET) != C_OK) {
413+
if (parseExtendedStringArgumentsOrReply(c, &flags, &unit, &expire, NULL, COMMAND_GET) != C_OK) {
381414
return;
382415
}
383416

tests/assets/test_cli_hint_suite.txt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,17 @@
6868
"ZRANGE k 1 2 WITHSCORES " "[BYSCORE|BYLEX] [REV] [LIMIT offset count]"
6969

7070
# Optional one-of args with parameters: SET key value [NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]
71-
"SET key value " "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
72-
"SET key value EX" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
73-
"SET key value EX " "seconds [NX|XX] [GET]"
74-
"SET key value EX 23 " "[NX|XX] [GET]"
75-
"SET key value EXAT" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
76-
"SET key value EXAT " "unix-time-seconds [NX|XX] [GET]"
77-
"SET key value PX" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
78-
"SET key value PX " "milliseconds [NX|XX] [GET]"
79-
"SET key value PXAT" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
80-
"SET key value PXAT " "unix-time-milliseconds [NX|XX] [GET]"
81-
"SET key value KEEPTTL " "[NX|XX] [GET]"
71+
"SET key value " "[NX|XX|IFEQ comparison-value] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
72+
"SET key value EX" "[NX|XX|IFEQ comparison-value] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
73+
"SET key value EX " "seconds [NX|XX|IFEQ comparison-value] [GET]"
74+
"SET key value EX 23 " "[NX|XX|IFEQ comparison-value] [GET]"
75+
"SET key value EXAT" "[NX|XX|IFEQ comparison-value] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
76+
"SET key value EXAT " "unix-time-seconds [NX|XX|IFEQ comparison-value] [GET]"
77+
"SET key value PX" "[NX|XX|IFEQ comparison-value] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
78+
"SET key value PX " "milliseconds [NX|XX|IFEQ comparison-value] [GET]"
79+
"SET key value PXAT" "[NX|XX|IFEQ comparison-value] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
80+
"SET key value PXAT " "unix-time-milliseconds [NX|XX|IFEQ comparison-value] [GET]"
81+
"SET key value KEEPTTL " "[NX|XX|IFEQ comparison-value] [GET]"
8282
"SET key value XX " "[GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
8383

8484
# If an input word can't be matched, stop hinting.

tests/unit/type/string.tcl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,56 @@ if {[string match {*jemalloc*} [s mem_allocator]]} {
582582
set err1
583583
} {*WRONGTYPE*}
584584

585+
test "SET with IFEQ conditional" {
586+
r del foo
587+
588+
r set foo "initial_value"
589+
590+
assert_equal {OK} [r set foo "new_value" ifeq "initial_value"]
591+
assert_equal "new_value" [r get foo]
592+
593+
assert_equal {} [r set foo "should_not_set" ifeq "wrong_value"]
594+
assert_equal "new_value" [r get foo]
595+
}
596+
597+
test "SET with IFEQ conditional - non-string current value" {
598+
r del foo
599+
600+
r sadd foo "some_set_value"
601+
assert_error {WRONGTYPE Operation against a key holding the wrong kind of value} {r set foo "new_value" ifeq "some_set_value"}
602+
}
603+
604+
605+
test "SET with IFEQ conditional - with get" {
606+
r del foo
607+
608+
assert_equal {} [r set foo "new_value" ifeq "initial_value" get]
609+
assert_equal {} [r get foo]
610+
611+
r set foo "initial_value"
612+
613+
assert_equal "initial_value" [r set foo "new_value" ifeq "initial_value" get]
614+
assert_equal "new_value" [r get foo]
615+
}
616+
617+
test "SET with IFEQ conditional - non string current value with get" {
618+
r del foo
619+
620+
r sadd foo "some_set_value"
621+
622+
assert_error {WRONGTYPE Operation against a key holding the wrong kind of value} {r set foo "new_value" ifeq "initial_value" get}
623+
}
624+
625+
test "SET with IFEQ conditional - with xx" {
626+
r del foo
627+
assert_error {ERR syntax error} {r set foo "new_value" ifeq "initial_value" xx}
628+
}
629+
630+
test "SET with IFEQ conditional - with nx" {
631+
r del foo
632+
assert_error {ERR syntax error} {r set foo "new_value" ifeq "initial_value" nx}
633+
}
634+
585635
test {Extended SET EX option} {
586636
r del foo
587637
r set foo bar ex 10

0 commit comments

Comments
 (0)