diff --git a/src/db.c b/src/db.c index 36650318..bd4ec4f6 100644 --- a/src/db.c +++ b/src/db.c @@ -62,7 +62,7 @@ robj *lookupKeyRead(redisDb *db, robj *key) { if (expireIfNeeded(db,key) == 1) { /* Key expired. If we are in the context of a master, expireIfNeeded() - * returns 0 only when the key does not exist at all, so it's save + * returns 0 only when the key does not exist at all, so it's safe * to return NULL ASAP. */ if (server.masterhost == NULL) return NULL; diff --git a/src/multi.c b/src/multi.c index c8287645..6d63db06 100644 --- a/src/multi.c +++ b/src/multi.c @@ -304,6 +304,9 @@ void touchWatchedKeysOnFlush(int dbid) { } } +/* WATCH key key key ... + * Add keys to the set of watched keys. If those keys are modified + * before the next transaction is executed, the transaction aborts. */ void watchCommand(redisClient *c) { int j; @@ -316,8 +319,47 @@ void watchCommand(redisClient *c) { addReply(c,shared.ok); } +/* UNWATCH + * Flush the set of watched keys. */ void unwatchCommand(redisClient *c) { unwatchAllKeys(c); c->flags &= (~REDIS_DIRTY_CAS); addReply(c,shared.ok); } + +/* IF XX key key key ... + * IF NX key key key ... + * Abort transaction if condition is not met. */ +void ifCommand(redisClient *c) { + int j; + int xx = 0, nx = 0; + + if (!(c->flags & REDIS_MULTI)) { + addReplyError(c,"IF outside MULTI is not allowed"); + return; + } + + /* Check if it's xx or nx option, trying to do it reasonably fast. */ + char *a = c->argv[1]->ptr; + if ((a[0] == 'n' || a[0] == 'N') && + (a[1] == 'x' || a[1] == 'X') && a[2] == '\0') + { + nx = 1; + } else if ((a[0] == 'x' || a[0] == 'X') && + (a[1] == 'x' || a[1] == 'X') && a[2] == '\0') + { + xx = 1; + } else { + addReply(c,shared.syntaxerr); + return; + } + + /* Check if keys exist / don't exist, and flag the transaction if + * at least one key fails the test according to NX / XX option. */ + for (j = 2; j < c->argc; j++) { + robj *o = lookupKeyRead(c->db,c->argv[j]); + if ((o != NULL && nx) || (o == NULL && xx)) + c->flags |= REDIS_DIRTY_CAS; + } + addReply(c,shared.ok); +} diff --git a/src/redis.c b/src/redis.c index 86b5a9eb..d8fd404f 100644 --- a/src/redis.c +++ b/src/redis.c @@ -261,6 +261,7 @@ struct redisCommand redisCommandTable[] = { {"pubsub",pubsubCommand,-2,"pltrR",0,NULL,0,0,0,0,0}, {"watch",watchCommand,-2,"rsF",0,NULL,1,-1,1,0,0}, {"unwatch",unwatchCommand,1,"rsF",0,NULL,0,0,0,0,0}, + {"if",ifCommand,-2,"rsF",0,NULL,2,-1,1,0,0}, {"cluster",clusterCommand,-2,"ar",0,NULL,0,0,0,0,0}, {"restore",restoreCommand,-4,"wm",0,NULL,1,1,1,0,0}, {"restore-asking",restoreCommand,-4,"wmk",0,NULL,1,1,1,0,0}, @@ -2336,7 +2337,7 @@ int processCommand(redisClient *c) { /* Exec the command */ if (c->flags & REDIS_MULTI && - c->cmd->proc != execCommand && c->cmd->proc != discardCommand && + c->cmd->proc != execCommand && c->cmd->proc != ifCommand && c->cmd->proc != multiCommand && c->cmd->proc != watchCommand) { queueMultiCommand(c); diff --git a/src/redis.h b/src/redis.h index 232ada5e..b4c7da01 100644 --- a/src/redis.h +++ b/src/redis.h @@ -1561,6 +1561,7 @@ void pfcountCommand(redisClient *c); void pfmergeCommand(redisClient *c); void pfdebugCommand(redisClient *c); void latencyCommand(redisClient *c); +void ifCommand(redisClient *c); #if defined(__GNUC__) void *calloc(size_t count, size_t size) __attribute__ ((deprecated));