summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Noordhuis <pcnoordhuis@gmail.com>2010-03-26 16:03:19 +0100
committerPieter Noordhuis <pcnoordhuis@gmail.com>2010-03-26 16:03:19 +0100
commit01426b054c615b58de73cd1bb418c1bd9fd1be8c (patch)
tree7bb453dc7283766a28894bac487ed1e6deba870d
parentb61a28fe35232ca34632e2ad1c755e21a6eb5426 (diff)
downloadredis-01426b054c615b58de73cd1bb418c1bd9fd1be8c.tar.gz
implements HINCRBY and tests (todo: find and fix small memleak)
-rw-r--r--redis-cli.c1
-rw-r--r--redis.c78
-rw-r--r--test-redis.tcl47
3 files changed, 126 insertions, 0 deletions
diff --git a/redis-cli.c b/redis-cli.c
index 1a45e0357..3f63aeaa6 100644
--- a/redis-cli.c
+++ b/redis-cli.c
@@ -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 025f9ddac..527b11ea0 100644
--- 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;
diff --git a/test-redis.tcl b/test-redis.tcl
index c8cb30193..436722544 100644
--- a/test-redis.tcl
+++ b/test-redis.tcl
@@ -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