Skip to content

Commit ed26e4b

Browse files
set with ifeq support
Signed-off-by: Sarthak Aggarwal <[email protected]>
1 parent ee386c9 commit ed26e4b

File tree

2 files changed

+46
-10
lines changed

2 files changed

+46
-10
lines changed

src/t_string.c

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ static int checkStringLength(client *c, long long size, long long append) {
7676
#define OBJ_EXAT (1 << 6) /* Set if timestamp in second is given */
7777
#define OBJ_PXAT (1 << 7) /* Set if timestamp in ms is given */
7878
#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;
@@ -102,6 +104,15 @@ void setGenericCommand(client *c,
102104

103105
found = (lookupKeyWrite(c->db, key) != NULL);
104106

107+
/* Handle the IFEQ conditional check */
108+
if ((flags & OBJ_SET_IFEQ) && found) {
109+
robj *current_value = lookupKeyRead(c->db, key);
110+
if (current_value == NULL || compareStringObjects(current_value, comparison) != 0) {
111+
addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
112+
return;
113+
}
114+
}
115+
105116
if ((flags & OBJ_SET_NX && found) || (flags & OBJ_SET_XX && !found)) {
106117
if (!(flags & OBJ_SET_GET)) {
107118
addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
@@ -208,7 +219,7 @@ static int getExpireMillisecondsOrReply(client *c, robj *expire, int flags, int
208219
* string arguments used in SET and GET command.
209220
*
210221
* Get specific commands - PERSIST/DEL
211-
* Set specific commands - XX/NX/GET
222+
* Set specific commands - XX/NX/GET/IFEQ
212223
* Common commands - EX/EXAT/PX/PXAT/KEEPTTL
213224
*
214225
* Function takes pointers to client, flags, unit, pointer to pointer of expire obj if needed
@@ -219,7 +230,7 @@ static int getExpireMillisecondsOrReply(client *c, robj *expire, int flags, int
219230
* Input flags are updated upon parsing the arguments. Unit and expire are updated if there are any
220231
* EX/EXAT/PX/PXAT arguments. Unit is updated to millisecond if PX/PXAT is set.
221232
*/
222-
int parseExtendedStringArgumentsOrReply(client *c, int *flags, int *unit, robj **expire, int command_type) {
233+
int parseExtendedStringArgumentsOrReply(client *c, int *flags, int *unit, robj **expire, robj **compare_val, int command_type) {
223234
int j = command_type == COMMAND_GET ? 2 : 3;
224235
for (; j < c->argc; j++) {
225236
char *opt = c->argv[j]->ptr;
@@ -295,7 +306,17 @@ int parseExtendedStringArgumentsOrReply(client *c, int *flags, int *unit, robj *
295306
*unit = UNIT_MILLISECONDS;
296307
*expire = next;
297308
j++;
298-
} else {
309+
} else if ((opt[0] == 'i' || opt[0] == 'I') &&
310+
(opt[1] == 'f' || opt[1] == 'F') &&
311+
(opt[2] == 'e' || opt[2] == 'E') &&
312+
(opt[3] == 'q' || opt[3] == 'Q') && opt[4] == '\0' &&
313+
next && (command_type == COMMAND_SET))
314+
{
315+
*flags |= OBJ_SET_IFEQ;
316+
*compare_val = next;
317+
j++;
318+
}
319+
else {
299320
addReplyErrorObject(c,shared.syntaxerr);
300321
return C_ERR;
301322
}
@@ -308,30 +329,31 @@ int parseExtendedStringArgumentsOrReply(client *c, int *flags, int *unit, robj *
308329
* [EXAT <seconds-timestamp>][PXAT <milliseconds-timestamp>] */
309330
void setCommand(client *c) {
310331
robj *expire = NULL;
332+
robj *comparison = NULL;
311333
int unit = UNIT_SECONDS;
312334
int flags = OBJ_NO_FLAGS;
313335

314-
if (parseExtendedStringArgumentsOrReply(c, &flags, &unit, &expire, COMMAND_SET) != C_OK) {
336+
if (parseExtendedStringArgumentsOrReply(c, &flags, &unit, &expire, &comparison, COMMAND_SET) != C_OK) {
315337
return;
316338
}
317339

318340
c->argv[2] = tryObjectEncoding(c->argv[2]);
319-
setGenericCommand(c, flags, c->argv[1], c->argv[2], expire, unit, NULL, NULL);
341+
setGenericCommand(c, flags, c->argv[1], c->argv[2], expire, unit, NULL, NULL, comparison);
320342
}
321343

322344
void setnxCommand(client *c) {
323345
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);
346+
setGenericCommand(c, OBJ_SET_NX, c->argv[1], c->argv[2], NULL, 0, shared.cone, shared.czero, NULL);
325347
}
326348

327349
void setexCommand(client *c) {
328350
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);
351+
setGenericCommand(c, OBJ_EX, c->argv[1], c->argv[3], c->argv[2], UNIT_SECONDS, NULL, NULL, NULL);
330352
}
331353

332354
void psetexCommand(client *c) {
333355
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);
356+
setGenericCommand(c, OBJ_PX, c->argv[1], c->argv[3], c->argv[2], UNIT_MILLISECONDS, NULL, NULL, NULL);
335357
}
336358

337359
int getGenericCommand(client *c) {
@@ -374,10 +396,11 @@ void getCommand(client *c) {
374396
*/
375397
void getexCommand(client *c) {
376398
robj *expire = NULL;
399+
robj *comparison = NULL;
377400
int unit = UNIT_SECONDS;
378401
int flags = OBJ_NO_FLAGS;
379402

380-
if (parseExtendedStringArgumentsOrReply(c, &flags, &unit, &expire, COMMAND_GET) != C_OK) {
403+
if (parseExtendedStringArgumentsOrReply(c, &flags, &unit, &expire, &comparison, COMMAND_GET) != C_OK) {
381404
return;
382405
}
383406

tests/unit/type/string.tcl

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

585+
test "SET with IFEQ conditional" {
586+
# Setting an initial value for the key
587+
r set foo "initial_value"
588+
589+
# Trying to set the key only if the value is exactly "initial_value"
590+
assert_equal OK [r set foo "new_value" ifeq "initial_value"]
591+
assert_equal "new_value" [r get foo]
592+
593+
# Trying to set the key only if the value is NOT "initial_value"
594+
assert_equal {} [r set foo "should_not_set" ifeq "wrong_value"]
595+
assert_equal "new_value" [r get foo]
596+
}
597+
585598
test {Extended SET EX option} {
586599
r del foo
587600
r set foo bar ex 10

0 commit comments

Comments
 (0)