diff --git a/redis-cli.c b/redis-cli.c index febc7cec0..b34c42773 100644 --- a/redis-cli.c +++ b/redis-cli.c @@ -150,9 +150,10 @@ static struct redisCommand cmdTable[] = { {"exec",1,REDIS_CMD_INLINE}, {"discard",1,REDIS_CMD_INLINE}, {"hset",4,REDIS_CMD_MULTIBULK}, - {"hmset",-4,REDIS_CMD_MULTIBULK}, - {"hincrby",4,REDIS_CMD_INLINE}, {"hget",3,REDIS_CMD_BULK}, + {"hmset",-4,REDIS_CMD_MULTIBULK}, + {"hmget",-3,REDIS_CMD_MULTIBULK}, + {"hincrby",4,REDIS_CMD_INLINE}, {"hdel",3,REDIS_CMD_BULK}, {"hlen",2,REDIS_CMD_INLINE}, {"hkeys",2,REDIS_CMD_INLINE}, diff --git a/redis.c b/redis.c index 6b23c7bc3..095a02e44 100644 --- a/redis.c +++ b/redis.c @@ -704,8 +704,9 @@ static void substrCommand(redisClient *c); static void zrankCommand(redisClient *c); static void zrevrankCommand(redisClient *c); static void hsetCommand(redisClient *c); -static void hmsetCommand(redisClient *c); static void hgetCommand(redisClient *c); +static void hmsetCommand(redisClient *c); +static void hmgetCommand(redisClient *c); static void hdelCommand(redisClient *c); static void hlenCommand(redisClient *c); static void zremrangebyrankCommand(redisClient *c); @@ -781,9 +782,10 @@ static struct redisCommand cmdTable[] = { {"zrank",zrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1}, {"zrevrank",zrevrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1}, {"hset",hsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"hmset",hmsetCommand,-4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1}, {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1}, + {"hmset",hmsetCommand,-4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1}, + {"hmget",hmgetCommand,-3,REDIS_CMD_BULK,NULL,1,1,1}, + {"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1}, {"hdel",hdelCommand,3,REDIS_CMD_BULK,NULL,1,1,1}, {"hlen",hlenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1}, {"hkeys",hkeysCommand,2,REDIS_CMD_INLINE,NULL,1,1,1}, @@ -6177,6 +6179,55 @@ static void hgetCommand(redisClient *c) { } } +static void hmgetCommand(redisClient *c) { + int i; + + robj *o = lookupKeyRead(c->db, c->argv[1]); + if (o == NULL) { + addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2)); + for (i = 2; i < c->argc; i++) { + addReply(c,shared.nullbulk); + } + return; + } else { + if (o->type != REDIS_HASH) { + addReply(c,shared.wrongtypeerr); + return; + } + } + + addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2)); + if (o->encoding == REDIS_ENCODING_ZIPMAP) { + unsigned char *zm = o->ptr; + unsigned char *v; + unsigned int vlen; + robj *field; + + for (i = 2; i < c->argc; i++) { + field = getDecodedObject(c->argv[i]); + if (zipmapGet(zm,field->ptr,sdslen(field->ptr),&v,&vlen)) { + addReplySds(c,sdscatprintf(sdsempty(),"$%u\r\n", vlen)); + addReplySds(c,sdsnewlen(v,vlen)); + addReply(c,shared.crlf); + } else { + addReply(c,shared.nullbulk); + } + decrRefCount(field); + } + } else { + dictEntry *de; + + for (i = 2; i < c->argc; i++) { + de = dictFind(o->ptr,c->argv[i]); + if (de != NULL) { + addReplyBulk(c,(robj*)dictGetEntryVal(de)); + } else { + addReply(c,shared.nullbulk); + } + } + } +} + static void hdelCommand(redisClient *c) { robj *o; int deleted = 0; diff --git a/redis.tcl b/redis.tcl index 233e2c9fe..b4a8ae22e 100644 --- a/redis.tcl +++ b/redis.tcl @@ -46,7 +46,7 @@ foreach redis_bulk_cmd { # Flag commands requiring last argument as a bulk write operation foreach redis_multibulk_cmd { - mset msetnx hset hmset + mset msetnx hset hmset hmget } { set ::redis::multibulkarg($redis_multibulk_cmd) {} } diff --git a/test-redis.tcl b/test-redis.tcl index c986baff7..6ebcb039f 100644 --- a/test-redis.tcl +++ b/test-redis.tcl @@ -1663,6 +1663,46 @@ proc main {server port} { $r hmset bighash {*}$args } {OK} + test {HMGET against non existing key and fields} { + set rv {} + lappend rv [$r hmget doesntexist __123123123__ __456456456__] + lappend rv [$r hmget smallhash __123123123__ __456456456__] + lappend rv [$r hmget bighash __123123123__ __456456456__] + set _ $rv + } {{{} {}} {{} {}} {{} {}}} + + test {HMGET - small hash} { + set keys {} + set vals {} + foreach {k v} [array get smallhash] { + lappend keys $k + lappend vals $v + } + set err {} + set result [$r hmget smallhash {*}$keys] + if {$vals ne $result} { + set err "$vals != $result" + break + } + set _ $err + } {} + + test {HMGET - big hash} { + set keys {} + set vals {} + foreach {k v} [array get bighash] { + lappend keys $k + lappend vals $v + } + set err {} + set result [$r hmget bighash {*}$keys] + if {$vals ne $result} { + set err "$vals != $result" + break + } + set _ $err + } {} + test {HKEYS - small hash} { lsort [$r hkeys smallhash] } [lsort [array names smallhash *]]