diff --git a/TODO b/TODO index 7722b34c..531b9738 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,10 @@ - * Expiring algorithm should be adaptive, if there are a lot of keys with an expire set and many of this happen to be already expired it should be, proportionally, more aggressive. +Pre 1.1 todo + +* For now only the last argument gets integer encoded, so make sure that: 1) every multi bulk commands implemented will have the last arg that is indeed a value, and not used otherwise. 2) to explicitly call the function to encode the object in MSET and other commands where there are multiple "values". + +After 1.1 todo + +* Expiring algorithm should be adaptive, if there are a lot of keys with an expire set and many of this happen to be already expired it should be, proportionally, more aggressive. * Add a command to inspect the currently selected DB index * Consistent hashing implemented in all the client libraries having an user base * SORT: Don't copy the list into a vector when BY argument is constant. diff --git a/client-libraries/tcl/redis.tcl b/client-libraries/tcl/redis.tcl index 636444de..deeadf2e 100644 --- a/client-libraries/tcl/redis.tcl +++ b/client-libraries/tcl/redis.tcl @@ -16,6 +16,7 @@ namespace eval redis {} set ::redis::id 0 array set ::redis::fd {} array set ::redis::bulkarg {} +array set ::redis::multibulkarg {} # Flag commands requiring last argument as a bulk write operation foreach redis_bulk_cmd { @@ -23,7 +24,16 @@ foreach redis_bulk_cmd { } { set ::redis::bulkarg($redis_bulk_cmd) {} } + +# Flag commands requiring last argument as a bulk write operation +foreach redis_multibulk_cmd { + mset +} { + set ::redis::multibulkarg($redis_multibulk_cmd) {} +} + unset redis_bulk_cmd +unset redis_multibulk_cmd proc redis {{server 127.0.0.1} {port 6379}} { set fd [socket $server $port] @@ -36,15 +46,24 @@ proc redis {{server 127.0.0.1} {port 6379}} { proc ::redis::__dispatch__ {id method args} { set fd $::redis::fd($id) if {[info command ::redis::__method__$method] eq {}} { - set cmd "$method " if {[info exists ::redis::bulkarg($method)]} { + set cmd "$method " append cmd [join [lrange $args 0 end-1]] append cmd " [string length [lindex $args end]]\r\n" append cmd [lindex $args end] + ::redis::redis_writenl $fd $cmd + } elseif {[info exists ::redis::multibulkarg($method)]} { + set cmd "*[expr {[llength $args]}+1]\r\n" + append cmd "$[string length $method]\r\n$method\r\n" + foreach a $args { + append cmd "$[string length $a]\r\n$a\r\n" + } + ::redis::redis_write $fd $cmd } else { + set cmd "$method " append cmd [join $args] + ::redis::redis_writenl $fd $cmd } - ::redis::redis_writenl $fd $cmd ::redis::redis_read_reply $fd } else { uplevel 1 [list ::redis::__method__$method $id $fd] $args diff --git a/redis.c b/redis.c index f36e3b08..cf2366fa 100644 --- a/redis.c +++ b/redis.c @@ -399,10 +399,13 @@ static void infoCommand(redisClient *c); static void mgetCommand(redisClient *c); static void monitorCommand(redisClient *c); static void expireCommand(redisClient *c); -static void getSetCommand(redisClient *c); +static void getsetCommand(redisClient *c); static void ttlCommand(redisClient *c); static void slaveofCommand(redisClient *c); static void debugCommand(redisClient *c); +static void msetCommand(redisClient *c); +static void msetnxCommand(redisClient *c); + /*================================= Globals ================================= */ /* Global vars */ @@ -441,7 +444,9 @@ static struct redisCommand cmdTable[] = { {"smembers",sinterCommand,2,REDIS_CMD_INLINE}, {"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, {"decrby",decrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, - {"getset",getSetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM}, + {"getset",getsetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM}, + {"mset",msetCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM}, + {"msetnx",msetnxCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM}, {"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE}, {"select",selectCommand,2,REDIS_CMD_INLINE}, {"move",moveCommand,3,REDIS_CMD_INLINE}, @@ -2596,7 +2601,7 @@ static void getCommand(redisClient *c) { } } -static void getSetCommand(redisClient *c) { +static void getsetCommand(redisClient *c) { getCommand(c); if (dictAdd(c->db->dict,c->argv[1],c->argv[2]) == DICT_ERR) { dictReplace(c->db->dict,c->argv[1],c->argv[2]); @@ -4094,6 +4099,42 @@ static void ttlCommand(redisClient *c) { addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",ttl)); } +static void msetGenericCommand(redisClient *c, int nx) { + int j; + + if ((c->argc % 2) == 0) { + addReplySds(c,sdsnew("-ERR wrong number of arguments\r\n")); + return; + } + /* Handle the NX flag. The MSETNX semantic is to return zero and don't + * set nothing at all if at least one already key exists. */ + if (nx) { + for (j = 1; j < c->argc; j += 2) { + if (dictFind(c->db->dict,c->argv[j]) != NULL) { + addReply(c, shared.czero); + return; + } + } + } + + for (j = 1; j < c->argc; j += 2) { + dictAdd(c->db->dict,c->argv[j],c->argv[j+1]); + incrRefCount(c->argv[j]); + incrRefCount(c->argv[j+1]); + removeExpire(c->db,c->argv[j]); + } + server.dirty += (c->argc-1)/2; + addReply(c, nx ? shared.cone : shared.ok); +} + +static void msetCommand(redisClient *c) { + msetGenericCommand(c,0); +} + +static void msetnxCommand(redisClient *c) { + msetGenericCommand(c,1); +} + /* =============================== Replication ============================= */ static int syncWrite(int fd, char *ptr, ssize_t size, int timeout) { @@ -4586,7 +4627,7 @@ static struct redisFunctionSym symsTable[] = { {"mgetCommand", (unsigned long)mgetCommand}, {"monitorCommand", (unsigned long)monitorCommand}, {"expireCommand", (unsigned long)expireCommand}, -{"getSetCommand", (unsigned long)getSetCommand}, +{"getsetCommand", (unsigned long)getsetCommand}, {"ttlCommand", (unsigned long)ttlCommand}, {"slaveofCommand", (unsigned long)slaveofCommand}, {"debugCommand", (unsigned long)debugCommand}, @@ -4594,6 +4635,9 @@ static struct redisFunctionSym symsTable[] = { {"setupSigSegvAction", (unsigned long)setupSigSegvAction}, {"readQueryFromClient", (unsigned long)readQueryFromClient}, {"rdbRemoveTempFile", (unsigned long)rdbRemoveTempFile}, +{"msetGenericCommand", (unsigned long)msetGenericCommand}, +{"msetCommand", (unsigned long)msetCommand}, +{"msetnxCommand", (unsigned long)msetnxCommand}, {NULL,0} };