@@ -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 */
8182static 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>] */
309330void 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
322344void 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
327349void 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
332354void 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
337359int getGenericCommand (client * c ) {
@@ -374,10 +396,11 @@ void getCommand(client *c) {
374396 */
375397void 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
0 commit comments