]> git.saurik.com Git - redis.git/commitdiff
implements HINCRBY and tests (todo: find and fix small memleak)
authorPieter Noordhuis <pcnoordhuis@gmail.com>
Fri, 26 Mar 2010 15:03:19 +0000 (16:03 +0100)
committerPieter Noordhuis <pcnoordhuis@gmail.com>
Fri, 26 Mar 2010 15:03:19 +0000 (16:03 +0100)
redis-cli.c
redis.c
test-redis.tcl

index 1a45e03571aa66c691ccddc29a82fd913dd8e624..3f63aeaa665870bd3bc3c4f998f49ef19ee4901c 100644 (file)
@@ -150,6 +150,7 @@ static struct redisCommand cmdTable[] = {
     {"exec",1,REDIS_CMD_INLINE},
     {"discard",1,REDIS_CMD_INLINE},
     {"hset",4,REDIS_CMD_MULTIBULK},
+    {"hincrby",4,REDIS_CMD_INLINE},
     {"hget",3,REDIS_CMD_BULK},
     {"hdel",3,REDIS_CMD_BULK},
     {"hlen",2,REDIS_CMD_INLINE},
diff --git a/redis.c b/redis.c
index 025f9ddac6fd3942a89fd945188359f71b84a2d6..527b11ea05f54f93a843aefa3d779cdef8327d1e 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -697,6 +697,7 @@ static void hvalsCommand(redisClient *c);
 static void hgetallCommand(redisClient *c);
 static void hexistsCommand(redisClient *c);
 static void configCommand(redisClient *c);
+static void hincrbyCommand(redisClient *c);
 
 /*================================= Globals ================================= */
 
@@ -756,6 +757,7 @@ 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},
+    {"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
     {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hdel",hdelCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hlen",hlenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
@@ -5954,6 +5956,82 @@ static void hsetCommand(redisClient *c) {
     addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",update == 0));
 }
 
+static void hincrbyCommand(redisClient *c) {
+    int update = 0;
+    long long value = 0, incr = 0;
+    robj *o = lookupKeyWrite(c->db,c->argv[1]);
+
+    if (o == NULL) {
+        o = createHashObject();
+        dictAdd(c->db->dict,c->argv[1],o);
+        incrRefCount(c->argv[1]);
+    } else {
+        if (o->type != REDIS_HASH) {
+            addReply(c,shared.wrongtypeerr);
+            return;
+        }
+    }
+
+    robj *o_incr = getDecodedObject(c->argv[3]);
+    incr = strtoll(o_incr->ptr, NULL, 10);
+    decrRefCount(o_incr);
+
+    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+        unsigned char *zm = o->ptr;
+        unsigned char *zval;
+        unsigned int zvlen;
+
+        /* Find value if already present in hash */
+        if (zipmapGet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
+            &zval,&zvlen)) {
+            /* strtoll needs the char* to have a trailing \0, but
+             * the zipmap doesn't include them. */
+            sds szval = sdsnewlen(zval, zvlen);
+            value = strtoll(szval,NULL,10);
+            sdsfree(szval);
+        }
+
+        value += incr;
+        sds svalue = sdscatprintf(sdsempty(),"%lld",value);
+        zm = zipmapSet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
+            (unsigned char*)svalue,sdslen(svalue),&update);
+        sdsfree(svalue);
+        o->ptr = zm;
+
+        /* Check if the zipmap needs to be converted
+         * if this was not an update. */
+        if (!update && zipmapLen(zm) > server.hash_max_zipmap_entries)
+            convertToRealHash(o);
+    } else {
+        robj *hval;
+        dictEntry *de;
+
+        /* Find value if already present in hash */
+        de = dictFind(o->ptr,c->argv[2]);
+        if (de != NULL) {
+            hval = dictGetEntryVal(de);
+            if (hval->encoding == REDIS_ENCODING_RAW)
+                value = strtoll(hval->ptr,NULL,10);
+            else if (hval->encoding == REDIS_ENCODING_INT)
+                value = (long)hval->ptr;
+            else
+                redisAssert(1 != 1);
+        }
+
+        value += incr;
+        hval = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
+        tryObjectEncoding(hval);
+        incrRefCount(hval);
+
+        if (dictReplace(o->ptr,c->argv[2],hval)) {
+            incrRefCount(c->argv[2]);
+        }
+    }
+
+    server.dirty++;
+    addReplyLong(c, value);
+}
+
 static void hgetCommand(redisClient *c) {
     robj *o;
 
index c8cb301933291c7efbcf30f64f51e265cffb2e53..43672254431887bbbef0e9e4a9809247314281e2 100644 (file)
@@ -1697,6 +1697,53 @@ proc main {server port} {
         $r debug object smallhash
     } {*hashtable*}
 
+    test {HINCRBY against non existing database key} {
+        $r del htest
+        list [$r hincrby htest foo 2]
+    } {2}
+
+    test {HINCRBY against non existing hash key} {
+        set rv {}
+        $r hdel smallhash tmp
+        $r hdel bighash tmp
+        lappend rv [$r hincrby smallhash tmp 2]
+        lappend rv [$r hget smallhash tmp]
+        lappend rv [$r hincrby bighash tmp 2]
+        lappend rv [$r hget bighash tmp]
+    } {2 2 2 2}
+
+    test {HINCRBY against hash key created by hincrby itself} {
+        set rv {}
+        lappend rv [$r hincrby smallhash tmp 3]
+        lappend rv [$r hget smallhash tmp]
+        lappend rv [$r hincrby bighash tmp 3]
+        lappend rv [$r hget bighash tmp]
+    } {5 5 5 5}
+
+    test {HINCRBY against hash key originally set with HSET} {
+        $r hset smallhash tmp 100
+        $r hset bighash tmp 100
+        list [$r hincrby smallhash tmp 2] [$r hincrby bighash tmp 2]
+    } {102 102}
+
+    test {HINCRBY over 32bit value} {
+        $r hset smallhash tmp 17179869184
+        $r hset bighash tmp 17179869184
+        list [$r hincrby smallhash tmp 1] [$r hincrby bighash tmp 1]
+    } {17179869185 17179869185}
+
+    test {HINCRBY over 32bit value with over 32bit increment} {
+        $r hset smallhash tmp 17179869184
+        $r hset bighash tmp 17179869184
+        list [$r hincrby smallhash tmp 17179869184] [$r hincrby bighash tmp 17179869184]
+    } {34359738368 34359738368}
+
+    test {HINCRBY against key with spaces (no integer encoded)} {
+        $r hset smallhash tmp "    11    "
+        $r hset bighash tmp "    11    "
+        list [$r hincrby smallhash tmp 1] [$r hincrby bighash tmp 1]
+    } {12 12}
+
     # TODO:
     # Randomized test, small and big
     # .rdb / AOF consistency test should include hashes