]> git.saurik.com Git - redis.git/blobdiff - redis.c
Merged gnrfan patches fixing issues 191, 193, 194
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index b61638f761d1b5eb097bbc4fa79483d0578d76a0..ec74c0018d6dd6fd6ae9dc41f8461976c0776e85 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -27,7 +27,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#define REDIS_VERSION "1.3.4"
+#define REDIS_VERSION "1.3.7"
 
 #include "fmacros.h"
 #include "config.h"
@@ -443,6 +443,10 @@ struct redisCommand {
     redisCommandProc *proc;
     int arity;
     int flags;
+    /* Use a function to determine which keys need to be loaded
+     * in the background prior to executing this command. Takes precedence
+     * over vm_firstkey and others, ignored when NULL */
+    redisCommandProc *vm_preload_proc;
     /* What keys should be loaded in background when calling this command? */
     int vm_firstkey; /* The first argument that's a key (0 = no keys) */
     int vm_lastkey;  /* THe last argument that's a key */
@@ -587,6 +591,7 @@ static robj *vmReadObjectFromSwap(off_t page, int type);
 static void waitEmptyIOJobsQueue(void);
 static void vmReopenSwapFile(void);
 static int vmFreePage(off_t page);
+static void zunionInterBlockClientOnSwappedKeys(redisClient *c);
 static int blockClientOnSwappedKeys(struct redisCommand *cmd, redisClient *c);
 static int dontWaitForSwappedKey(redisClient *c, robj *key);
 static void handleClientsBlockedOnSwappedKey(redisDb *db, robj *key);
@@ -682,108 +687,120 @@ static void zrevrankCommand(redisClient *c);
 static void hsetCommand(redisClient *c);
 static void hgetCommand(redisClient *c);
 static void hdelCommand(redisClient *c);
+static void hlenCommand(redisClient *c);
 static void zremrangebyrankCommand(redisClient *c);
 static void zunionCommand(redisClient *c);
 static void zinterCommand(redisClient *c);
+static void hkeysCommand(redisClient *c);
+static void hvalsCommand(redisClient *c);
+static void hgetallCommand(redisClient *c);
+static void hexistsCommand(redisClient *c);
 
 /*================================= Globals ================================= */
 
 /* Global vars */
 static struct redisServer server; /* server global state */
 static struct redisCommand cmdTable[] = {
-    {"get",getCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"set",setCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,0,0,0},
-    {"setnx",setnxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,0,0,0},
-    {"append",appendCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"substr",substrCommand,4,REDIS_CMD_INLINE,1,1,1},
-    {"del",delCommand,-2,REDIS_CMD_INLINE,0,0,0},
-    {"exists",existsCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"incr",incrCommand,2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1},
-    {"decr",decrCommand,2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1},
-    {"mget",mgetCommand,-2,REDIS_CMD_INLINE,1,-1,1},
-    {"rpush",rpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"lpush",lpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"rpop",rpopCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"lpop",lpopCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"brpop",brpopCommand,-3,REDIS_CMD_INLINE,1,1,1},
-    {"blpop",blpopCommand,-3,REDIS_CMD_INLINE,1,1,1},
-    {"llen",llenCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"lindex",lindexCommand,3,REDIS_CMD_INLINE,1,1,1},
-    {"lset",lsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"lrange",lrangeCommand,4,REDIS_CMD_INLINE,1,1,1},
-    {"ltrim",ltrimCommand,4,REDIS_CMD_INLINE,1,1,1},
-    {"lrem",lremCommand,4,REDIS_CMD_BULK,1,1,1},
-    {"rpoplpush",rpoplpushcommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,2,1},
-    {"sadd",saddCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"srem",sremCommand,3,REDIS_CMD_BULK,1,1,1},
-    {"smove",smoveCommand,4,REDIS_CMD_BULK,1,2,1},
-    {"sismember",sismemberCommand,3,REDIS_CMD_BULK,1,1,1},
-    {"scard",scardCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"spop",spopCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"srandmember",srandmemberCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"sinter",sinterCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,-1,1},
-    {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,2,-1,1},
-    {"sunion",sunionCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,-1,1},
-    {"sunionstore",sunionstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,2,-1,1},
-    {"sdiff",sdiffCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,-1,1},
-    {"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,2,-1,1},
-    {"smembers",sinterCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"zadd",zaddCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"zincrby",zincrbyCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"zrem",zremCommand,3,REDIS_CMD_BULK,1,1,1},
-    {"zremrangebyscore",zremrangebyscoreCommand,4,REDIS_CMD_INLINE,1,1,1},
-    {"zremrangebyrank",zremrangebyrankCommand,4,REDIS_CMD_INLINE,1,1,1},
-    {"zunion",zunionCommand,-4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,0,0,0},
-    {"zinter",zinterCommand,-4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,0,0,0},
-    {"zrange",zrangeCommand,-4,REDIS_CMD_INLINE,1,1,1},
-    {"zrangebyscore",zrangebyscoreCommand,-4,REDIS_CMD_INLINE,1,1,1},
-    {"zcount",zcountCommand,4,REDIS_CMD_INLINE,1,1,1},
-    {"zrevrange",zrevrangeCommand,-4,REDIS_CMD_INLINE,1,1,1},
-    {"zcard",zcardCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"zscore",zscoreCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"zrank",zrankCommand,3,REDIS_CMD_INLINE,1,1,1},
-    {"zrevrank",zrevrankCommand,3,REDIS_CMD_INLINE,1,1,1},
-    {"hset",hsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"hget",hgetCommand,3,REDIS_CMD_BULK,1,1,1},
-    {"hdel",hdelCommand,3,REDIS_CMD_BULK,1,1,1},
-    {"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1},
-    {"decrby",decrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1},
-    {"getset",getsetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
-    {"mset",msetCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,-1,2},
-    {"msetnx",msetnxCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,-1,2},
-    {"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"select",selectCommand,2,REDIS_CMD_INLINE,0,0,0},
-    {"move",moveCommand,3,REDIS_CMD_INLINE,1,1,1},
-    {"rename",renameCommand,3,REDIS_CMD_INLINE,1,1,1},
-    {"renamenx",renamenxCommand,3,REDIS_CMD_INLINE,1,1,1},
-    {"expire",expireCommand,3,REDIS_CMD_INLINE,0,0,0},
-    {"expireat",expireatCommand,3,REDIS_CMD_INLINE,0,0,0},
-    {"keys",keysCommand,2,REDIS_CMD_INLINE,0,0,0},
-    {"dbsize",dbsizeCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"auth",authCommand,2,REDIS_CMD_INLINE,0,0,0},
-    {"ping",pingCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"echo",echoCommand,2,REDIS_CMD_BULK,0,0,0},
-    {"save",saveCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"bgsave",bgsaveCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"bgrewriteaof",bgrewriteaofCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"shutdown",shutdownCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"lastsave",lastsaveCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"type",typeCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"multi",multiCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"exec",execCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"discard",discardCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"sync",syncCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"flushdb",flushdbCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"flushall",flushallCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"sort",sortCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1},
-    {"info",infoCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"monitor",monitorCommand,1,REDIS_CMD_INLINE,0,0,0},
-    {"ttl",ttlCommand,2,REDIS_CMD_INLINE,1,1,1},
-    {"slaveof",slaveofCommand,3,REDIS_CMD_INLINE,0,0,0},
-    {"debug",debugCommand,-2,REDIS_CMD_INLINE,0,0,0},
-    {NULL,NULL,0,0,0,0,0}
+    {"get",getCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"set",setCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
+    {"setnx",setnxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
+    {"append",appendCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"substr",substrCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"del",delCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"exists",existsCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"incr",incrCommand,2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"decr",decrCommand,2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"mget",mgetCommand,-2,REDIS_CMD_INLINE,NULL,1,-1,1},
+    {"rpush",rpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"lpush",lpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"rpop",rpopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"lpop",lpopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"brpop",brpopCommand,-3,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"blpop",blpopCommand,-3,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"llen",llenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"lindex",lindexCommand,3,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"lset",lsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"lrange",lrangeCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"ltrim",ltrimCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"lrem",lremCommand,4,REDIS_CMD_BULK,NULL,1,1,1},
+    {"rpoplpush",rpoplpushcommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,2,1},
+    {"sadd",saddCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"srem",sremCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
+    {"smove",smoveCommand,4,REDIS_CMD_BULK,NULL,1,2,1},
+    {"sismember",sismemberCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
+    {"scard",scardCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"spop",spopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"srandmember",srandmemberCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"sinter",sinterCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,-1,1},
+    {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,2,-1,1},
+    {"sunion",sunionCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,-1,1},
+    {"sunionstore",sunionstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,2,-1,1},
+    {"sdiff",sdiffCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,-1,1},
+    {"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,2,-1,1},
+    {"smembers",sinterCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"zadd",zaddCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"zincrby",zincrbyCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"zrem",zremCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
+    {"zremrangebyscore",zremrangebyscoreCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"zremrangebyrank",zremrangebyrankCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"zunion",zunionCommand,-4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
+    {"zinter",zinterCommand,-4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
+    {"zrange",zrangeCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"zrangebyscore",zrangebyscoreCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"zcount",zcountCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"zrevrange",zrevrangeCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"zcard",zcardCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"zscore",zscoreCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"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},
+    {"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},
+    {"hkeys",hkeysCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"hvals",hvalsCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"hgetall",hgetallCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"hexists",hexistsCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
+    {"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"decrby",decrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"getset",getsetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"mset",msetCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,-1,2},
+    {"msetnx",msetnxCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,-1,2},
+    {"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"select",selectCommand,2,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"move",moveCommand,3,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"rename",renameCommand,3,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"renamenx",renamenxCommand,3,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"expire",expireCommand,3,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"expireat",expireatCommand,3,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"keys",keysCommand,2,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"dbsize",dbsizeCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"auth",authCommand,2,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"ping",pingCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"echo",echoCommand,2,REDIS_CMD_BULK,NULL,0,0,0},
+    {"save",saveCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"bgsave",bgsaveCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"bgrewriteaof",bgrewriteaofCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"shutdown",shutdownCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"lastsave",lastsaveCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"type",typeCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"multi",multiCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"exec",execCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"discard",discardCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"sync",syncCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"flushdb",flushdbCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"flushall",flushallCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"sort",sortCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"info",infoCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"monitor",monitorCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"ttl",ttlCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
+    {"slaveof",slaveofCommand,3,REDIS_CMD_INLINE,NULL,0,0,0},
+    {"debug",debugCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
+    {NULL,NULL,0,0,NULL,0,0,0}
 };
 
+static void usage();
+
 /*============================ Utility functions ============================ */
 
 /* Glob-style pattern matching. */
@@ -990,6 +1007,10 @@ static int dictEncObjKeyCompare(void *privdata, const void *key1,
     robj *o1 = (robj*) key1, *o2 = (robj*) key2;
     int cmp;
 
+    if (o1->encoding == REDIS_ENCODING_INT &&
+        o2->encoding == REDIS_ENCODING_INT &&
+        o1->ptr == o2->ptr) return 1;
+
     o1 = getDecodedObject(o1);
     o2 = getDecodedObject(o2);
     cmp = sdsDictKeyCompare(privdata,o1->ptr,o2->ptr);
@@ -1083,6 +1104,8 @@ static dictType keylistDictType = {
     dictListDestructor          /* val destructor */
 };
 
+static void version();
+
 /* ========================= Random utility functions ======================= */
 
 /* Redis generally does not try to recover from out of memory conditions
@@ -1595,12 +1618,15 @@ static void loadServerConfig(char *filename) {
     char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL;
     int linenum = 0;
     sds line = NULL;
+    char *errormsg = "Fatal error, can't open config file '%s'";
+    char *errorbuf = zmalloc(sizeof(char)*(strlen(errormsg)+strlen(filename)));
+    sprintf(errorbuf, errormsg, filename);
 
     if (filename[0] == '-' && filename[1] == '\0')
         fp = stdin;
     else {
         if ((fp = fopen(filename,"r")) == NULL) {
-            redisLog(REDIS_WARNING,"Fatal error, can't open config file");
+            redisLog(REDIS_WARNING, errorbuf);
             exit(1);
         }
     }
@@ -1682,6 +1708,8 @@ static void loadServerConfig(char *filename) {
             if (server.dbnum < 1) {
                 err = "Invalid number of databases"; goto loaderr;
             }
+        } else if (!strcasecmp(argv[0],"include") && argc == 2) {
+            loadServerConfig(argv[1]);
         } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
             server.maxclients = atoi(argv[1]);
         } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
@@ -2476,10 +2504,32 @@ static void addReplyLong(redisClient *c, long l) {
     char buf[128];
     size_t len;
 
+    if (l == 0) {
+        addReply(c,shared.czero);
+        return;
+    } else if (l == 1) {
+        addReply(c,shared.cone);
+        return;
+    }
     len = snprintf(buf,sizeof(buf),":%ld\r\n",l);
     addReplySds(c,sdsnewlen(buf,len));
 }
 
+static void addReplyUlong(redisClient *c, unsigned long ul) {
+    char buf[128];
+    size_t len;
+
+    if (ul == 0) {
+        addReply(c,shared.czero);
+        return;
+    } else if (ul == 1) {
+        addReply(c,shared.cone);
+        return;
+    }
+    len = snprintf(buf,sizeof(buf),":%lu\r\n",ul);
+    addReplySds(c,sdsnewlen(buf,len));
+}
+
 static void addReplyBulkLen(redisClient *c, robj *obj) {
     size_t len;
 
@@ -2501,6 +2551,12 @@ static void addReplyBulkLen(redisClient *c, robj *obj) {
     addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n",(unsigned long)len));
 }
 
+static void addReplyBulk(redisClient *c, robj *obj) {
+    addReplyBulkLen(c,obj);
+    addReply(c,obj);
+    addReply(c,shared.crlf);
+}
+
 static void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
     int cport, cfd;
     char cip[128];
@@ -2684,7 +2740,7 @@ static void decrRefCount(void *obj) {
         case REDIS_SET: freeSetObject(o); break;
         case REDIS_ZSET: freeZsetObject(o); break;
         case REDIS_HASH: freeHashObject(o); break;
-        default: redisAssert(0 != 0); break;
+        default: redisAssert(0); break;
         }
         if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
         if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX ||
@@ -2739,6 +2795,26 @@ static robj *lookupKeyWrite(redisDb *db, robj *key) {
     return lookupKey(db,key);
 }
 
+static robj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply) {
+    robj *o = lookupKeyRead(c->db, key);
+    if (!o) addReply(c,reply);
+    return o;
+}
+
+static robj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply) {
+    robj *o = lookupKeyWrite(c->db, key);
+    if (!o) addReply(c,reply);
+    return o;
+}
+
+static int checkType(redisClient *c, robj *o, int type) {
+    if (o->type != type) {
+        addReply(c,shared.wrongtypeerr);
+        return 1;
+    }
+    return 0;
+}
+
 static int deleteKey(redisDb *db, robj *key) {
     int retval;
 
@@ -3162,7 +3238,7 @@ static int rdbSaveObject(FILE *fp, robj *o) {
             dictReleaseIterator(di);
         }
     } else {
-        redisAssert(0 != 0);
+        redisAssert(0);
     }
     return 0;
 }
@@ -3382,7 +3458,7 @@ static robj *rdbLoadIntegerObject(FILE *fp, int enctype) {
         val = (int32_t)v;
     } else {
         val = 0; /* anti-warning */
-        redisAssert(0!=0);
+        redisAssert(0);
     }
     return createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",val));
 }
@@ -3421,7 +3497,7 @@ static robj *rdbLoadStringObject(FILE*fp) {
         case REDIS_RDB_ENC_LZF:
             return tryObjectSharing(rdbLoadLzfStringObject(fp));
         default:
-            redisAssert(0!=0);
+            redisAssert(0);
         }
     }
 
@@ -3540,12 +3616,10 @@ static robj *rdbLoadObject(int type, FILE *fp) {
                 tryObjectEncoding(key);
                 tryObjectEncoding(val);
                 dictAdd((dict*)o->ptr,key,val);
-                incrRefCount(key);
-                incrRefCount(val);
             }
         }
     } else {
-        redisAssert(0 != 0);
+        redisAssert(0);
     }
     return o;
 }
@@ -3652,9 +3726,7 @@ static void pingCommand(redisClient *c) {
 }
 
 static void echoCommand(redisClient *c) {
-    addReplyBulkLen(c,c->argv[1]);
-    addReply(c,c->argv[1]);
-    addReply(c,shared.crlf);
+    addReplyBulk(c,c->argv[1]);
 }
 
 /*=================================== Strings =============================== */
@@ -3696,21 +3768,17 @@ static void setnxCommand(redisClient *c) {
 }
 
 static int getGenericCommand(redisClient *c) {
-    robj *o = lookupKeyRead(c->db,c->argv[1]);
-
-    if (o == NULL) {
-        addReply(c,shared.nullbulk);
+    robj *o;
+    
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
         return REDIS_OK;
+
+    if (o->type != REDIS_STRING) {
+        addReply(c,shared.wrongtypeerr);
+        return REDIS_ERR;
     } else {
-        if (o->type != REDIS_STRING) {
-            addReply(c,shared.wrongtypeerr);
-            return REDIS_ERR;
-        } else {
-            addReplyBulkLen(c,o);
-            addReply(c,o);
-            addReply(c,shared.crlf);
-            return REDIS_OK;
-        }
+        addReplyBulk(c,o);
+        return REDIS_OK;
     }
 }
 
@@ -3742,9 +3810,7 @@ static void mgetCommand(redisClient *c) {
             if (o->type != REDIS_STRING) {
                 addReply(c,shared.nullbulk);
             } else {
-                addReplyBulkLen(c,o);
-                addReply(c,o);
-                addReply(c,shared.crlf);
+                addReplyBulk(c,o);
             }
         }
     }
@@ -3904,44 +3970,37 @@ static void substrCommand(redisClient *c) {
     robj *o;
     long start = atoi(c->argv[2]->ptr);
     long end = atoi(c->argv[3]->ptr);
+    size_t rangelen, strlen;
+    sds range;
 
-    o = lookupKeyRead(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.nullbulk);
-    } else {
-        if (o->type != REDIS_STRING) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            size_t rangelen, strlen;
-            sds range;
-
-            o = getDecodedObject(o);
-            strlen = sdslen(o->ptr);
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
+        checkType(c,o,REDIS_STRING)) return;
 
-            /* convert negative indexes */
-            if (start < 0) start = strlen+start;
-            if (end < 0) end = strlen+end;
-            if (start < 0) start = 0;
-            if (end < 0) end = 0;
+    o = getDecodedObject(o);
+    strlen = sdslen(o->ptr);
 
-            /* indexes sanity checks */
-            if (start > end || (size_t)start >= strlen) {
-                /* Out of range start or start > end result in null reply */
-                addReply(c,shared.nullbulk);
-                decrRefCount(o);
-                return;
-            }
-            if ((size_t)end >= strlen) end = strlen-1;
-            rangelen = (end-start)+1;
+    /* convert negative indexes */
+    if (start < 0) start = strlen+start;
+    if (end < 0) end = strlen+end;
+    if (start < 0) start = 0;
+    if (end < 0) end = 0;
 
-            /* Return the result */
-            addReplySds(c,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen));
-            range = sdsnewlen((char*)o->ptr+start,rangelen);
-            addReplySds(c,range);
-            addReply(c,shared.crlf);
-            decrRefCount(o);
-        }
+    /* indexes sanity checks */
+    if (start > end || (size_t)start >= strlen) {
+        /* Out of range start or start > end result in null reply */
+        addReply(c,shared.nullbulk);
+        decrRefCount(o);
+        return;
     }
+    if ((size_t)end >= strlen) end = strlen-1;
+    rangelen = (end-start)+1;
+
+    /* Return the result */
+    addReplySds(c,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen));
+    range = sdsnewlen((char*)o->ptr+start,rangelen);
+    addReplySds(c,range);
+    addReply(c,shared.crlf);
+    decrRefCount(o);
 }
 
 /* ========================= Type agnostic commands ========================= */
@@ -3955,17 +4014,7 @@ static void delCommand(redisClient *c) {
             deleted++;
         }
     }
-    switch(deleted) {
-    case 0:
-        addReply(c,shared.czero);
-        break;
-    case 1:
-        addReply(c,shared.cone);
-        break;
-    default:
-        addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",deleted));
-        break;
-    }
+    addReplyLong(c,deleted);
 }
 
 static void existsCommand(redisClient *c) {
@@ -4017,9 +4066,7 @@ static void keysCommand(redisClient *c) {
         if ((pattern[0] == '*' && pattern[1] == '\0') ||
             stringmatchlen(pattern,plen,key,sdslen(key),0)) {
             if (expireIfNeeded(c->db,keyobj) == 0) {
-                addReplyBulkLen(c,keyobj);
-                addReply(c,keyobj);
-                addReply(c,shared.crlf);
+                addReplyBulk(c,keyobj);
                 numkeys++;
             }
         }
@@ -4109,12 +4156,14 @@ static void shutdownCommand(redisClient *c) {
             if (server.vm_enabled) unlink(server.vm_swap_file);
             exit(0);
         } else {
-            /* Ooops.. error saving! The best we can do is to continue operating.
-             * Note that if there was a background saving process, in the next
-             * cron() Redis will be notified that the background saving aborted,
-             * handling special stuff like slaves pending for synchronization... */
+            /* Ooops.. error saving! The best we can do is to continue
+             * operating. Note that if there was a background saving process,
+             * in the next cron() Redis will be notified that the background
+             * saving aborted, handling special stuff like slaves pending for
+             * synchronization... */
             redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit"); 
-            addReplySds(c,sdsnew("-ERR can't quit, problems saving the DB\r\n"));
+            addReplySds(c,
+                sdsnew("-ERR can't quit, problems saving the DB\r\n"));
         }
     }
 }
@@ -4128,11 +4177,9 @@ static void renameGenericCommand(redisClient *c, int nx) {
         return;
     }
 
-    o = lookupKeyWrite(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.nokeyerr);
+    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL)
         return;
-    }
+
     incrRefCount(o);
     deleteIfVolatile(c->db,c->argv[2]);
     if (dictAdd(c->db->dict,c->argv[2],o) == DICT_ERR) {
@@ -4255,107 +4302,78 @@ static void rpushCommand(redisClient *c) {
 static void llenCommand(redisClient *c) {
     robj *o;
     list *l;
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,o,REDIS_LIST)) return;
     
-    o = lookupKeyRead(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.czero);
-        return;
-    } else {
-        if (o->type != REDIS_LIST) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            l = o->ptr;
-            addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",listLength(l)));
-        }
-    }
+    l = o->ptr;
+    addReplyUlong(c,listLength(l));
 }
 
 static void lindexCommand(redisClient *c) {
     robj *o;
     int index = atoi(c->argv[2]->ptr);
-    
-    o = lookupKeyRead(c->db,c->argv[1]);
-    if (o == NULL) {
+    list *list;
+    listNode *ln;
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
+        checkType(c,o,REDIS_LIST)) return;
+    list = o->ptr;
+
+    ln = listIndex(list, index);
+    if (ln == NULL) {
         addReply(c,shared.nullbulk);
     } else {
-        if (o->type != REDIS_LIST) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            list *list = o->ptr;
-            listNode *ln;
-            
-            ln = listIndex(list, index);
-            if (ln == NULL) {
-                addReply(c,shared.nullbulk);
-            } else {
-                robj *ele = listNodeValue(ln);
-                addReplyBulkLen(c,ele);
-                addReply(c,ele);
-                addReply(c,shared.crlf);
-            }
-        }
+        robj *ele = listNodeValue(ln);
+        addReplyBulk(c,ele);
     }
 }
 
 static void lsetCommand(redisClient *c) {
     robj *o;
     int index = atoi(c->argv[2]->ptr);
-    
-    o = lookupKeyWrite(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.nokeyerr);
+    list *list;
+    listNode *ln;
+
+    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL ||
+        checkType(c,o,REDIS_LIST)) return;
+    list = o->ptr;
+
+    ln = listIndex(list, index);
+    if (ln == NULL) {
+        addReply(c,shared.outofrangeerr);
     } else {
-        if (o->type != REDIS_LIST) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            list *list = o->ptr;
-            listNode *ln;
-            
-            ln = listIndex(list, index);
-            if (ln == NULL) {
-                addReply(c,shared.outofrangeerr);
-            } else {
-                robj *ele = listNodeValue(ln);
+        robj *ele = listNodeValue(ln);
 
-                decrRefCount(ele);
-                listNodeValue(ln) = c->argv[3];
-                incrRefCount(c->argv[3]);
-                addReply(c,shared.ok);
-                server.dirty++;
-            }
-        }
+        decrRefCount(ele);
+        listNodeValue(ln) = c->argv[3];
+        incrRefCount(c->argv[3]);
+        addReply(c,shared.ok);
+        server.dirty++;
     }
 }
 
 static void popGenericCommand(redisClient *c, int where) {
     robj *o;
+    list *list;
+    listNode *ln;
 
-    o = lookupKeyWrite(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.nullbulk);
-    } else {
-        if (o->type != REDIS_LIST) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            list *list = o->ptr;
-            listNode *ln;
+    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
+        checkType(c,o,REDIS_LIST)) return;
+    list = o->ptr;
 
-            if (where == REDIS_HEAD)
-                ln = listFirst(list);
-            else
-                ln = listLast(list);
+    if (where == REDIS_HEAD)
+        ln = listFirst(list);
+    else
+        ln = listLast(list);
 
-            if (ln == NULL) {
-                addReply(c,shared.nullbulk);
-            } else {
-                robj *ele = listNodeValue(ln);
-                addReplyBulkLen(c,ele);
-                addReply(c,ele);
-                addReply(c,shared.crlf);
-                listDelNode(list,ln);
-                server.dirty++;
-            }
-        }
+    if (ln == NULL) {
+        addReply(c,shared.nullbulk);
+    } else {
+        robj *ele = listNodeValue(ln);
+        addReplyBulk(c,ele);
+        listDelNode(list,ln);
+        server.dirty++;
     }
 }
 
@@ -4371,46 +4389,39 @@ static void lrangeCommand(redisClient *c) {
     robj *o;
     int start = atoi(c->argv[2]->ptr);
     int end = atoi(c->argv[3]->ptr);
+    int llen;
+    int rangelen, j;
+    list *list;
+    listNode *ln;
+    robj *ele;
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullmultibulk)) == NULL ||
+        checkType(c,o,REDIS_LIST)) return;
+    list = o->ptr;
+    llen = listLength(list);
+
+    /* convert negative indexes */
+    if (start < 0) start = llen+start;
+    if (end < 0) end = llen+end;
+    if (start < 0) start = 0;
+    if (end < 0) end = 0;
+
+    /* indexes sanity checks */
+    if (start > end || start >= llen) {
+        /* Out of range start or start > end result in empty list */
+        addReply(c,shared.emptymultibulk);
+        return;
+    }
+    if (end >= llen) end = llen-1;
+    rangelen = (end-start)+1;
 
-    o = lookupKeyRead(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.nullmultibulk);
-    } else {
-        if (o->type != REDIS_LIST) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            list *list = o->ptr;
-            listNode *ln;
-            int llen = listLength(list);
-            int rangelen, j;
-            robj *ele;
-
-            /* convert negative indexes */
-            if (start < 0) start = llen+start;
-            if (end < 0) end = llen+end;
-            if (start < 0) start = 0;
-            if (end < 0) end = 0;
-
-            /* indexes sanity checks */
-            if (start > end || start >= llen) {
-                /* Out of range start or start > end result in empty list */
-                addReply(c,shared.emptymultibulk);
-                return;
-            }
-            if (end >= llen) end = llen-1;
-            rangelen = (end-start)+1;
-
-            /* Return the result in form of a multi-bulk reply */
-            ln = listIndex(list, start);
-            addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen));
-            for (j = 0; j < rangelen; j++) {
-                ele = listNodeValue(ln);
-                addReplyBulkLen(c,ele);
-                addReply(c,ele);
-                addReply(c,shared.crlf);
-                ln = ln->next;
-            }
-        }
+    /* Return the result in form of a multi-bulk reply */
+    ln = listIndex(list, start);
+    addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen));
+    for (j = 0; j < rangelen; j++) {
+        ele = listNodeValue(ln);
+        addReplyBulk(c,ele);
+        ln = ln->next;
     }
 }
 
@@ -4418,87 +4429,76 @@ static void ltrimCommand(redisClient *c) {
     robj *o;
     int start = atoi(c->argv[2]->ptr);
     int end = atoi(c->argv[3]->ptr);
-    
-    o = lookupKeyWrite(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.ok);
+    int llen;
+    int j, ltrim, rtrim;
+    list *list;
+    listNode *ln;
+
+    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok)) == NULL ||
+        checkType(c,o,REDIS_LIST)) return;
+    list = o->ptr;
+    llen = listLength(list);
+
+    /* convert negative indexes */
+    if (start < 0) start = llen+start;
+    if (end < 0) end = llen+end;
+    if (start < 0) start = 0;
+    if (end < 0) end = 0;
+
+    /* indexes sanity checks */
+    if (start > end || start >= llen) {
+        /* Out of range start or start > end result in empty list */
+        ltrim = llen;
+        rtrim = 0;
     } else {
-        if (o->type != REDIS_LIST) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            list *list = o->ptr;
-            listNode *ln;
-            int llen = listLength(list);
-            int j, ltrim, rtrim;
-
-            /* convert negative indexes */
-            if (start < 0) start = llen+start;
-            if (end < 0) end = llen+end;
-            if (start < 0) start = 0;
-            if (end < 0) end = 0;
-
-            /* indexes sanity checks */
-            if (start > end || start >= llen) {
-                /* Out of range start or start > end result in empty list */
-                ltrim = llen;
-                rtrim = 0;
-            } else {
-                if (end >= llen) end = llen-1;
-                ltrim = start;
-                rtrim = llen-end-1;
-            }
+        if (end >= llen) end = llen-1;
+        ltrim = start;
+        rtrim = llen-end-1;
+    }
 
-            /* Remove list elements to perform the trim */
-            for (j = 0; j < ltrim; j++) {
-                ln = listFirst(list);
-                listDelNode(list,ln);
-            }
-            for (j = 0; j < rtrim; j++) {
-                ln = listLast(list);
-                listDelNode(list,ln);
-            }
-            server.dirty++;
-            addReply(c,shared.ok);
-        }
+    /* Remove list elements to perform the trim */
+    for (j = 0; j < ltrim; j++) {
+        ln = listFirst(list);
+        listDelNode(list,ln);
+    }
+    for (j = 0; j < rtrim; j++) {
+        ln = listLast(list);
+        listDelNode(list,ln);
     }
+    server.dirty++;
+    addReply(c,shared.ok);
 }
 
 static void lremCommand(redisClient *c) {
     robj *o;
-    
-    o = lookupKeyWrite(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.czero);
-    } else {
-        if (o->type != REDIS_LIST) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            list *list = o->ptr;
-            listNode *ln, *next;
-            int toremove = atoi(c->argv[2]->ptr);
-            int removed = 0;
-            int fromtail = 0;
-
-            if (toremove < 0) {
-                toremove = -toremove;
-                fromtail = 1;
-            }
-            ln = fromtail ? list->tail : list->head;
-            while (ln) {
-                robj *ele = listNodeValue(ln);
-
-                next = fromtail ? ln->prev : ln->next;
-                if (compareStringObjects(ele,c->argv[3]) == 0) {
-                    listDelNode(list,ln);
-                    server.dirty++;
-                    removed++;
-                    if (toremove && removed == toremove) break;
-                }
-                ln = next;
-            }
-            addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",removed));
+    list *list;
+    listNode *ln, *next;
+    int toremove = atoi(c->argv[2]->ptr);
+    int removed = 0;
+    int fromtail = 0;
+
+    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,o,REDIS_LIST)) return;
+    list = o->ptr;
+
+    if (toremove < 0) {
+        toremove = -toremove;
+        fromtail = 1;
+    }
+    ln = fromtail ? list->tail : list->head;
+    while (ln) {
+        robj *ele = listNodeValue(ln);
+
+        next = fromtail ? ln->prev : ln->next;
+        if (compareStringObjects(ele,c->argv[3]) == 0) {
+            listDelNode(list,ln);
+            server.dirty++;
+            removed++;
+            if (toremove && removed == toremove) break;
         }
+        ln = next;
     }
+    addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",removed));
 }
 
 /* This is the semantic of this command:
@@ -4518,57 +4518,49 @@ static void lremCommand(redisClient *c) {
  */
 static void rpoplpushcommand(redisClient *c) {
     robj *sobj;
+    list *srclist;
+    listNode *ln;
 
-    sobj = lookupKeyWrite(c->db,c->argv[1]);
-    if (sobj == NULL) {
+    if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
+        checkType(c,sobj,REDIS_LIST)) return;
+    srclist = sobj->ptr;
+    ln = listLast(srclist);
+
+    if (ln == NULL) {
         addReply(c,shared.nullbulk);
     } else {
-        if (sobj->type != REDIS_LIST) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            list *srclist = sobj->ptr;
-            listNode *ln = listLast(srclist);
-
-            if (ln == NULL) {
-                addReply(c,shared.nullbulk);
-            } else {
-                robj *dobj = lookupKeyWrite(c->db,c->argv[2]);
-                robj *ele = listNodeValue(ln);
-                list *dstlist;
+        robj *dobj = lookupKeyWrite(c->db,c->argv[2]);
+        robj *ele = listNodeValue(ln);
+        list *dstlist;
 
-                if (dobj && dobj->type != REDIS_LIST) {
-                    addReply(c,shared.wrongtypeerr);
-                    return;
-                }
-
-                /* Add the element to the target list (unless it's directly
-                 * passed to some BLPOP-ing client */
-                if (!handleClientsWaitingListPush(c,c->argv[2],ele)) {
-                    if (dobj == NULL) {
-                        /* Create the list if the key does not exist */
-                        dobj = createListObject();
-                        dictAdd(c->db->dict,c->argv[2],dobj);
-                        incrRefCount(c->argv[2]);
-                    }
-                    dstlist = dobj->ptr;
-                    listAddNodeHead(dstlist,ele);
-                    incrRefCount(ele);
-                }
-
-                /* Send the element to the client as reply as well */
-                addReplyBulkLen(c,ele);
-                addReply(c,ele);
-                addReply(c,shared.crlf);
+        if (dobj && dobj->type != REDIS_LIST) {
+            addReply(c,shared.wrongtypeerr);
+            return;
+        }
 
-                /* Finally remove the element from the source list */
-                listDelNode(srclist,ln);
-                server.dirty++;
+        /* Add the element to the target list (unless it's directly
+         * passed to some BLPOP-ing client */
+        if (!handleClientsWaitingListPush(c,c->argv[2],ele)) {
+            if (dobj == NULL) {
+                /* Create the list if the key does not exist */
+                dobj = createListObject();
+                dictAdd(c->db->dict,c->argv[2],dobj);
+                incrRefCount(c->argv[2]);
             }
+            dstlist = dobj->ptr;
+            listAddNodeHead(dstlist,ele);
+            incrRefCount(ele);
         }
+
+        /* Send the element to the client as reply as well */
+        addReplyBulk(c,ele);
+
+        /* Finally remove the element from the source list */
+        listDelNode(srclist,ln);
+        server.dirty++;
     }
 }
 
-
 /* ==================================== Sets ================================ */
 
 static void saddCommand(redisClient *c) {
@@ -4597,21 +4589,15 @@ static void saddCommand(redisClient *c) {
 static void sremCommand(redisClient *c) {
     robj *set;
 
-    set = lookupKeyWrite(c->db,c->argv[1]);
-    if (set == NULL) {
-        addReply(c,shared.czero);
+    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,set,REDIS_SET)) return;
+
+    if (dictDelete(set->ptr,c->argv[2]) == DICT_OK) {
+        server.dirty++;
+        if (htNeedsResize(set->ptr)) dictResize(set->ptr);
+        addReply(c,shared.cone);
     } else {
-        if (set->type != REDIS_SET) {
-            addReply(c,shared.wrongtypeerr);
-            return;
-        }
-        if (dictDelete(set->ptr,c->argv[2]) == DICT_OK) {
-            server.dirty++;
-            if (htNeedsResize(set->ptr)) dictResize(set->ptr);
-            addReply(c,shared.cone);
-        } else {
-            addReply(c,shared.czero);
-        }
+        addReply(c,shared.czero);
     }
 }
 
@@ -4653,65 +4639,43 @@ static void smoveCommand(redisClient *c) {
 static void sismemberCommand(redisClient *c) {
     robj *set;
 
-    set = lookupKeyRead(c->db,c->argv[1]);
-    if (set == NULL) {
+    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,set,REDIS_SET)) return;
+
+    if (dictFind(set->ptr,c->argv[2]))
+        addReply(c,shared.cone);
+    else
         addReply(c,shared.czero);
-    } else {
-        if (set->type != REDIS_SET) {
-            addReply(c,shared.wrongtypeerr);
-            return;
-        }
-        if (dictFind(set->ptr,c->argv[2]))
-            addReply(c,shared.cone);
-        else
-            addReply(c,shared.czero);
-    }
 }
 
 static void scardCommand(redisClient *c) {
     robj *o;
     dict *s;
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,o,REDIS_SET)) return;
     
-    o = lookupKeyRead(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.czero);
-        return;
-    } else {
-        if (o->type != REDIS_SET) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            s = o->ptr;
-            addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",
-                dictSize(s)));
-        }
-    }
+    s = o->ptr;
+    addReplyUlong(c,dictSize(s));
 }
 
 static void spopCommand(redisClient *c) {
     robj *set;
     dictEntry *de;
 
-    set = lookupKeyWrite(c->db,c->argv[1]);
-    if (set == NULL) {
+    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
+        checkType(c,set,REDIS_SET)) return;
+
+    de = dictGetRandomKey(set->ptr);
+    if (de == NULL) {
         addReply(c,shared.nullbulk);
     } else {
-        if (set->type != REDIS_SET) {
-            addReply(c,shared.wrongtypeerr);
-            return;
-        }
-        de = dictGetRandomKey(set->ptr);
-        if (de == NULL) {
-            addReply(c,shared.nullbulk);
-        } else {
-            robj *ele = dictGetEntryKey(de);
+        robj *ele = dictGetEntryKey(de);
 
-            addReplyBulkLen(c,ele);
-            addReply(c,ele);
-            addReply(c,shared.crlf);
-            dictDelete(set->ptr,ele);
-            if (htNeedsResize(set->ptr)) dictResize(set->ptr);
-            server.dirty++;
-        }
+        addReplyBulk(c,ele);
+        dictDelete(set->ptr,ele);
+        if (htNeedsResize(set->ptr)) dictResize(set->ptr);
+        server.dirty++;
     }
 }
 
@@ -4719,24 +4683,16 @@ static void srandmemberCommand(redisClient *c) {
     robj *set;
     dictEntry *de;
 
-    set = lookupKeyRead(c->db,c->argv[1]);
-    if (set == NULL) {
+    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
+        checkType(c,set,REDIS_SET)) return;
+
+    de = dictGetRandomKey(set->ptr);
+    if (de == NULL) {
         addReply(c,shared.nullbulk);
     } else {
-        if (set->type != REDIS_SET) {
-            addReply(c,shared.wrongtypeerr);
-            return;
-        }
-        de = dictGetRandomKey(set->ptr);
-        if (de == NULL) {
-            addReply(c,shared.nullbulk);
-        } else {
-            robj *ele = dictGetEntryKey(de);
+        robj *ele = dictGetEntryKey(de);
 
-            addReplyBulkLen(c,ele);
-            addReply(c,ele);
-            addReply(c,shared.crlf);
-        }
+        addReplyBulk(c,ele);
     }
 }
 
@@ -4810,9 +4766,7 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long
             continue; /* at least one set does not contain the member */
         ele = dictGetEntryKey(de);
         if (!dstkey) {
-            addReplyBulkLen(c,ele);
-            addReply(c,ele);
-            addReply(c,shared.crlf);
+            addReplyBulk(c,ele);
             cardinality++;
         } else {
             dictAdd(dstset->ptr,ele,NULL);
@@ -4917,9 +4871,7 @@ static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnu
             robj *ele;
 
             ele = dictGetEntryKey(de);
-            addReplyBulkLen(c,ele);
-            addReply(c,ele);
-            addReply(c,shared.crlf);
+            addReplyBulk(c,ele);
         }
         dictReleaseIterator(di);
     } else {
@@ -5254,11 +5206,11 @@ zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {
 
     x = zsl->header;
     for (i = zsl->level-1; i >= 0; i--) {
-        while (x->forward[i] && (traversed + (i > 0 ? x->span[i-1] : 1)) <= rank) {
+        while (x->forward[i] && (traversed + (i>0 ? x->span[i-1] : 1)) <= rank)
+        {
             traversed += i > 0 ? x->span[i-1] : 1;
             x = x->forward[i];
         }
-
         if (traversed == rank) {
             return x;
         }
@@ -5366,101 +5318,80 @@ static void zincrbyCommand(redisClient *c) {
 static void zremCommand(redisClient *c) {
     robj *zsetobj;
     zset *zs;
+    dictEntry *de;
+    double *oldscore;
+    int deleted;
 
-    zsetobj = lookupKeyWrite(c->db,c->argv[1]);
-    if (zsetobj == NULL) {
-        addReply(c,shared.czero);
-    } else {
-        dictEntry *de;
-        double *oldscore;
-        int deleted;
-
-        if (zsetobj->type != REDIS_ZSET) {
-            addReply(c,shared.wrongtypeerr);
-            return;
-        }
-        zs = zsetobj->ptr;
-        de = dictFind(zs->dict,c->argv[2]);
-        if (de == NULL) {
-            addReply(c,shared.czero);
-            return;
-        }
-        /* Delete from the skiplist */
-        oldscore = dictGetEntryVal(de);
-        deleted = zslDelete(zs->zsl,*oldscore,c->argv[2]);
-        redisAssert(deleted != 0);
+    if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,zsetobj,REDIS_ZSET)) return;
 
-        /* Delete from the hash table */
-        dictDelete(zs->dict,c->argv[2]);
-        if (htNeedsResize(zs->dict)) dictResize(zs->dict);
-        server.dirty++;
-        addReply(c,shared.cone);
+    zs = zsetobj->ptr;
+    de = dictFind(zs->dict,c->argv[2]);
+    if (de == NULL) {
+        addReply(c,shared.czero);
+        return;
     }
+    /* Delete from the skiplist */
+    oldscore = dictGetEntryVal(de);
+    deleted = zslDelete(zs->zsl,*oldscore,c->argv[2]);
+    redisAssert(deleted != 0);
+
+    /* Delete from the hash table */
+    dictDelete(zs->dict,c->argv[2]);
+    if (htNeedsResize(zs->dict)) dictResize(zs->dict);
+    server.dirty++;
+    addReply(c,shared.cone);
 }
 
 static void zremrangebyscoreCommand(redisClient *c) {
     double min = strtod(c->argv[2]->ptr,NULL);
     double max = strtod(c->argv[3]->ptr,NULL);
+    long deleted;
     robj *zsetobj;
     zset *zs;
 
-    zsetobj = lookupKeyWrite(c->db,c->argv[1]);
-    if (zsetobj == NULL) {
-        addReply(c,shared.czero);
-    } else {
-        long deleted;
+    if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,zsetobj,REDIS_ZSET)) return;
 
-        if (zsetobj->type != REDIS_ZSET) {
-            addReply(c,shared.wrongtypeerr);
-            return;
-        }
-        zs = zsetobj->ptr;
-        deleted = zslDeleteRangeByScore(zs->zsl,min,max,zs->dict);
-        if (htNeedsResize(zs->dict)) dictResize(zs->dict);
-        server.dirty += deleted;
-        addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",deleted));
-    }
+    zs = zsetobj->ptr;
+    deleted = zslDeleteRangeByScore(zs->zsl,min,max,zs->dict);
+    if (htNeedsResize(zs->dict)) dictResize(zs->dict);
+    server.dirty += deleted;
+    addReplyLong(c,deleted);
 }
 
 static void zremrangebyrankCommand(redisClient *c) {
     int start = atoi(c->argv[2]->ptr);
     int end = atoi(c->argv[3]->ptr);
+    int llen;
+    long deleted;
     robj *zsetobj;
     zset *zs;
 
-    zsetobj = lookupKeyWrite(c->db,c->argv[1]);
-    if (zsetobj == NULL) {
-        addReply(c,shared.czero);
-    } else {
-        if (zsetobj->type != REDIS_ZSET) {
-            addReply(c,shared.wrongtypeerr);
-            return;
-        }
-
-        zs = zsetobj->ptr;
-        int llen = zs->zsl->length;
-        long deleted;
-
-        /* convert negative indexes */
-        if (start < 0) start = llen+start;
-        if (end < 0) end = llen+end;
-        if (start < 0) start = 0;
-        if (end < 0) end = 0;
+    if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,zsetobj,REDIS_ZSET)) return;
+    zs = zsetobj->ptr;
+    llen = zs->zsl->length;
 
-        /* indexes sanity checks */
-        if (start > end || start >= llen) {
-            addReply(c,shared.czero);
-            return;
-        }
-        if (end >= llen) end = llen-1;
+    /* convert negative indexes */
+    if (start < 0) start = llen+start;
+    if (end < 0) end = llen+end;
+    if (start < 0) start = 0;
+    if (end < 0) end = 0;
 
-        /* increment start and end because zsl*Rank functions
-         * use 1-based rank */
-        deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);
-        if (htNeedsResize(zs->dict)) dictResize(zs->dict);
-        server.dirty += deleted;
-        addReplyLong(c, deleted);
+    /* indexes sanity checks */
+    if (start > end || start >= llen) {
+        addReply(c,shared.czero);
+        return;
     }
+    if (end >= llen) end = llen-1;
+
+    /* increment start and end because zsl*Rank functions
+     * use 1-based rank */
+    deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);
+    if (htNeedsResize(zs->dict)) dictResize(zs->dict);
+    server.dirty += deleted;
+    addReplyLong(c, deleted);
 }
 
 typedef struct {
@@ -5476,8 +5407,26 @@ static int qsortCompareZsetopsrcByCardinality(const void *s1, const void *s2) {
     return size1 - size2;
 }
 
+#define REDIS_AGGR_SUM 1
+#define REDIS_AGGR_MIN 2
+#define REDIS_AGGR_MAX 3
+
+inline static void zunionInterAggregate(double *target, double val, int aggregate) {
+    if (aggregate == REDIS_AGGR_SUM) {
+        *target = *target + val;
+    } else if (aggregate == REDIS_AGGR_MIN) {
+        *target = val < *target ? val : *target;
+    } else if (aggregate == REDIS_AGGR_MAX) {
+        *target = val > *target ? val : *target;
+    } else {
+        /* safety net */
+        redisAssert(0 != 0);
+    }
+}
+
 static void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
     int i, j, zsetnum;
+    int aggregate = REDIS_AGGR_SUM;
     zsetopsrc *src;
     robj *dstobj;
     zset *dstzset;
@@ -5518,19 +5467,28 @@ static void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
 
     /* parse optional extra arguments */
     if (j < c->argc) {
-        int remaining = c->argc-j;
+        int remaining = c->argc - j;
 
         while (remaining) {
-            if (!strcasecmp(c->argv[j]->ptr,"weights")) {
+            if (remaining >= (zsetnum + 1) && !strcasecmp(c->argv[j]->ptr,"weights")) {
                 j++; remaining--;
-                if (remaining < zsetnum) {
-                    zfree(src);
-                    addReplySds(c,sdsnew("-ERR not enough weights for ZUNION/ZINTER\r\n"));
-                    return;
-                }
                 for (i = 0; i < zsetnum; i++, j++, remaining--) {
                     src[i].weight = strtod(c->argv[j]->ptr, NULL);
                 }
+            } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,"aggregate")) {
+                j++; remaining--;
+                if (!strcasecmp(c->argv[j]->ptr,"sum")) {
+                    aggregate = REDIS_AGGR_SUM;
+                } else if (!strcasecmp(c->argv[j]->ptr,"min")) {
+                    aggregate = REDIS_AGGR_MIN;
+                } else if (!strcasecmp(c->argv[j]->ptr,"max")) {
+                    aggregate = REDIS_AGGR_MAX;
+                } else {
+                    zfree(src);
+                    addReply(c,shared.syntaxerr);
+                    return;
+                }
+                j++; remaining--;
             } else {
                 zfree(src);
                 addReply(c,shared.syntaxerr);
@@ -5539,27 +5497,28 @@ static void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
         }
     }
 
+    /* sort sets from the smallest to largest, this will improve our
+     * algorithm's performance */
+    qsort(src,zsetnum,sizeof(zsetopsrc), qsortCompareZsetopsrcByCardinality);
+
     dstobj = createZsetObject();
     dstzset = dstobj->ptr;
 
     if (op == REDIS_OP_INTER) {
-        /* sort sets from the smallest to largest, this will improve our
-         * algorithm's performance */
-        qsort(src,zsetnum,sizeof(zsetopsrc), qsortCompareZsetopsrcByCardinality);
-
         /* skip going over all entries if the smallest zset is NULL or empty */
         if (src[0].dict && dictSize(src[0].dict) > 0) {
             /* precondition: as src[0].dict is non-empty and the zsets are ordered
              * from small to large, all src[i > 0].dict are non-empty too */
             di = dictGetIterator(src[0].dict);
             while((de = dictNext(di)) != NULL) {
-                double *score = zmalloc(sizeof(double));
-                *score = 0.0;
+                double *score = zmalloc(sizeof(double)), value;
+                *score = src[0].weight * (*(double*)dictGetEntryVal(de));
 
-                for (j = 0; j < zsetnum; j++) {
-                    dictEntry *other = (j == 0) ? de : dictFind(src[j].dict,dictGetEntryKey(de));
+                for (j = 1; j < zsetnum; j++) {
+                    dictEntry *other = dictFind(src[j].dict,dictGetEntryKey(de));
                     if (other) {
-                        *score = *score + src[j].weight * (*(double*)dictGetEntryVal(other));
+                        value = src[j].weight * (*(double*)dictGetEntryVal(other));
+                        zunionInterAggregate(score, value, aggregate);
                     } else {
                         break;
                     }
@@ -5587,14 +5546,16 @@ static void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
                 /* skip key when already processed */
                 if (dictFind(dstzset->dict,dictGetEntryKey(de)) != NULL) continue;
 
-                double *score = zmalloc(sizeof(double));
-                *score = 0.0;
-                for (j = 0; j < zsetnum; j++) {
-                    if (!src[j].dict) continue;
+                double *score = zmalloc(sizeof(double)), value;
+                *score = src[i].weight * (*(double*)dictGetEntryVal(de));
 
-                    dictEntry *other = (i == j) ? de : dictFind(src[j].dict,dictGetEntryKey(de));
+                /* because the zsets are sorted by size, its only possible
+                 * for sets at larger indices to hold this entry */
+                for (j = (i+1); j < zsetnum; j++) {
+                    dictEntry *other = dictFind(src[j].dict,dictGetEntryKey(de));
                     if (other) {
-                        *score = *score + src[j].weight * (*(double*)dictGetEntryVal(other));
+                        value = src[j].weight * (*(double*)dictGetEntryVal(other));
+                        zunionInterAggregate(score, value, aggregate);
                     }
                 }
 
@@ -5633,6 +5594,12 @@ static void zrangeGenericCommand(redisClient *c, int reverse) {
     int start = atoi(c->argv[2]->ptr);
     int end = atoi(c->argv[3]->ptr);
     int withscores = 0;
+    int llen;
+    int rangelen, j;
+    zset *zsetobj;
+    zskiplist *zsl;
+    zskiplistNode *ln;
+    robj *ele;
 
     if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores")) {
         withscores = 1;
@@ -5641,57 +5608,45 @@ static void zrangeGenericCommand(redisClient *c, int reverse) {
         return;
     }
 
-    o = lookupKeyRead(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.nullmultibulk);
-    } else {
-        if (o->type != REDIS_ZSET) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            zset *zsetobj = o->ptr;
-            zskiplist *zsl = zsetobj->zsl;
-            zskiplistNode *ln;
-
-            int llen = zsl->length;
-            int rangelen, j;
-            robj *ele;
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullmultibulk)) == NULL ||
+        checkType(c,o,REDIS_ZSET)) return;
+    zsetobj = o->ptr;
+    zsl = zsetobj->zsl;
+    llen = zsl->length;
 
-            /* convert negative indexes */
-            if (start < 0) start = llen+start;
-            if (end < 0) end = llen+end;
-            if (start < 0) start = 0;
-            if (end < 0) end = 0;
+    /* convert negative indexes */
+    if (start < 0) start = llen+start;
+    if (end < 0) end = llen+end;
+    if (start < 0) start = 0;
+    if (end < 0) end = 0;
 
-            /* indexes sanity checks */
-            if (start > end || start >= llen) {
-                /* Out of range start or start > end result in empty list */
-                addReply(c,shared.emptymultibulk);
-                return;
-            }
-            if (end >= llen) end = llen-1;
-            rangelen = (end-start)+1;
+    /* indexes sanity checks */
+    if (start > end || start >= llen) {
+        /* Out of range start or start > end result in empty list */
+        addReply(c,shared.emptymultibulk);
+        return;
+    }
+    if (end >= llen) end = llen-1;
+    rangelen = (end-start)+1;
 
-            /* check if starting point is trivial, before searching
-             * the element in log(N) time */
-            if (reverse) {
-                ln = start == 0 ? zsl->tail : zslGetElementByRank(zsl, llen-start);
-            } else {
-                ln = start == 0 ? zsl->header->forward[0] : zslGetElementByRank(zsl, start+1);
-            }
+    /* check if starting point is trivial, before searching
+     * the element in log(N) time */
+    if (reverse) {
+        ln = start == 0 ? zsl->tail : zslGetElementByRank(zsl, llen-start);
+    } else {
+        ln = start == 0 ?
+            zsl->header->forward[0] : zslGetElementByRank(zsl, start+1);
+    }
 
-            /* Return the result in form of a multi-bulk reply */
-            addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",
-                withscores ? (rangelen*2) : rangelen));
-            for (j = 0; j < rangelen; j++) {
-                ele = ln->obj;
-                addReplyBulkLen(c,ele);
-                addReply(c,ele);
-                addReply(c,shared.crlf);
-                if (withscores)
-                    addReplyDouble(c,ln->score);
-                ln = reverse ? ln->backward : ln->forward[0];
-            }
-        }
+    /* Return the result in form of a multi-bulk reply */
+    addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",
+        withscores ? (rangelen*2) : rangelen));
+    for (j = 0; j < rangelen; j++) {
+        ele = ln->obj;
+        addReplyBulk(c,ele);
+        if (withscores)
+            addReplyDouble(c,ln->score);
+        ln = reverse ? ln->backward : ln->forward[0];
     }
 }
 
@@ -5801,9 +5756,7 @@ static void genericZrangebyscoreCommand(redisClient *c, int justcount) {
                 if (limit == 0) break;
                 if (!justcount) {
                     ele = ln->obj;
-                    addReplyBulkLen(c,ele);
-                    addReply(c,ele);
-                    addReply(c,shared.crlf);
+                    addReplyBulk(c,ele);
                     if (withscores)
                         addReplyDouble(c,ln->score);
                 }
@@ -5832,80 +5785,62 @@ static void zcountCommand(redisClient *c) {
 static void zcardCommand(redisClient *c) {
     robj *o;
     zset *zs;
-    
-    o = lookupKeyRead(c->db,c->argv[1]);
-    if (o == NULL) {
-        addReply(c,shared.czero);
-        return;
-    } else {
-        if (o->type != REDIS_ZSET) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            zs = o->ptr;
-            addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",zs->zsl->length));
-        }
-    }
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,o,REDIS_ZSET)) return;
+
+    zs = o->ptr;
+    addReplyUlong(c,zs->zsl->length);
 }
 
 static void zscoreCommand(redisClient *c) {
     robj *o;
     zset *zs;
-    
-    o = lookupKeyRead(c->db,c->argv[1]);
-    if (o == NULL) {
+    dictEntry *de;
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
+        checkType(c,o,REDIS_ZSET)) return;
+
+    zs = o->ptr;
+    de = dictFind(zs->dict,c->argv[2]);
+    if (!de) {
         addReply(c,shared.nullbulk);
-        return;
     } else {
-        if (o->type != REDIS_ZSET) {
-            addReply(c,shared.wrongtypeerr);
-        } else {
-            dictEntry *de;
-
-            zs = o->ptr;
-            de = dictFind(zs->dict,c->argv[2]);
-            if (!de) {
-                addReply(c,shared.nullbulk);
-            } else {
-                double *score = dictGetEntryVal(de);
+        double *score = dictGetEntryVal(de);
 
-                addReplyDouble(c,*score);
-            }
-        }
+        addReplyDouble(c,*score);
     }
 }
 
 static void zrankGenericCommand(redisClient *c, int reverse) {
     robj *o;
-    o = lookupKeyRead(c->db,c->argv[1]);
-    if (o == NULL) {
+    zset *zs;
+    zskiplist *zsl;
+    dictEntry *de;
+    unsigned long rank;
+    double *score;
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
+        checkType(c,o,REDIS_ZSET)) return;
+
+    zs = o->ptr;
+    zsl = zs->zsl;
+    de = dictFind(zs->dict,c->argv[2]);
+    if (!de) {
         addReply(c,shared.nullbulk);
         return;
     }
-    if (o->type != REDIS_ZSET) {
-        addReply(c,shared.wrongtypeerr);
-    } else {
-        zset *zs = o->ptr;
-        zskiplist *zsl = zs->zsl;
-        dictEntry *de;
-        unsigned long rank;
 
-        de = dictFind(zs->dict,c->argv[2]);
-        if (!de) {
-            addReply(c,shared.nullbulk);
-            return;
-        }
-
-        double *score = dictGetEntryVal(de);
-        rank = zslGetRank(zsl, *score, c->argv[2]);
-        if (rank) {
-            if (reverse) {
-                addReplyLong(c, zsl->length - rank);
-            } else {
-                addReplyLong(c, rank-1);
-            }
+    score = dictGetEntryVal(de);
+    rank = zslGetRank(zsl, *score, c->argv[2]);
+    if (rank) {
+        if (reverse) {
+            addReplyLong(c, zsl->length - rank);
         } else {
-            addReply(c,shared.nullbulk);
+            addReplyLong(c, rank-1);
         }
+    } else {
+        addReply(c,shared.nullbulk);
     }
 }
 
@@ -5932,6 +5867,20 @@ static void hsetCommand(redisClient *c) {
             return;
         }
     }
+    /* We want to convert the zipmap into an hash table right now if the
+     * entry to be added is too big. Note that we check if the object
+     * is integer encoded before to try fetching the length in the test below.
+     * This is because integers are small, but currently stringObjectLen()
+     * performs a slow conversion: not worth it. */
+    if (o->encoding == REDIS_ENCODING_ZIPMAP &&
+        ((c->argv[2]->encoding == REDIS_ENCODING_RAW &&
+          sdslen(c->argv[2]->ptr) > server.hash_max_zipmap_value) ||
+         (c->argv[3]->encoding == REDIS_ENCODING_RAW &&
+          sdslen(c->argv[3]->ptr) > server.hash_max_zipmap_value)))
+    {
+        convertToRealHash(o);
+    }
+
     if (o->encoding == REDIS_ENCODING_ZIPMAP) {
         unsigned char *zm = o->ptr;
         robj *valobj = getDecodedObject(c->argv[3]);
@@ -5940,8 +5889,17 @@ static void hsetCommand(redisClient *c) {
             valobj->ptr,sdslen(valobj->ptr),&update);
         decrRefCount(valobj);
         o->ptr = zm;
+
+        /* And here there is the second check for hash conversion...
+         * we want to do it only if the operation was not just an update as
+         * zipmapLen() is O(N). */
+        if (!update && zipmapLen(zm) > server.hash_max_zipmap_entries)
+            convertToRealHash(o);
     } else {
-        if (dictAdd(o->ptr,c->argv[2],c->argv[3]) == DICT_OK) {
+        tryObjectEncoding(c->argv[2]);
+        /* note that c->argv[3] is already encoded, as the latest arg
+         * of a bulk command is always integer encoded if possible. */
+        if (dictReplace(o->ptr,c->argv[2],c->argv[3])) {
             incrRefCount(c->argv[2]);
         } else {
             update = 1;
@@ -5953,71 +5911,162 @@ static void hsetCommand(redisClient *c) {
 }
 
 static void hgetCommand(redisClient *c) {
-    robj *o = lookupKeyRead(c->db,c->argv[1]);
+    robj *o;
 
-    if (o == NULL) {
-        addReply(c,shared.nullbulk);
-        return;
-    } else {
-        if (o->type != REDIS_HASH) {
-            addReply(c,shared.wrongtypeerr);
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
+        checkType(c,o,REDIS_HASH)) return;
+
+    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+        unsigned char *zm = o->ptr;
+        unsigned char *val;
+        unsigned int vlen;
+        robj *field;
+
+        field = getDecodedObject(c->argv[2]);
+        if (zipmapGet(zm,field->ptr,sdslen(field->ptr), &val,&vlen)) {
+            addReplySds(c,sdscatprintf(sdsempty(),"$%u\r\n", vlen));
+            addReplySds(c,sdsnewlen(val,vlen));
+            addReply(c,shared.crlf);
+            decrRefCount(field);
+            return;
+        } else {
+            addReply(c,shared.nullbulk);
+            decrRefCount(field);
             return;
         }
+    } else {
+        struct dictEntry *de;
 
-        if (o->encoding == REDIS_ENCODING_ZIPMAP) {
-            unsigned char *zm = o->ptr;
-            unsigned char *val;
-            unsigned int vlen;
-
-            if (zipmapGet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr), &val,&vlen)) {
-                addReplySds(c,sdscatprintf(sdsempty(),"$%u\r\n", vlen));
-                addReplySds(c,sdsnewlen(val,vlen));
-                addReply(c,shared.crlf);
-                return;
-            } else {
-                addReply(c,shared.nullbulk);
-                return;
-            }
+        de = dictFind(o->ptr,c->argv[2]);
+        if (de == NULL) {
+            addReply(c,shared.nullbulk);
         } else {
-            struct dictEntry *de;
-
-            de = dictFind(o->ptr,c->argv[2]);
-            if (de == NULL) {
-                addReply(c,shared.nullbulk);
-            } else {
-                robj *e = dictGetEntryVal(de);
+            robj *e = dictGetEntryVal(de);
 
-                addReplyBulkLen(c,e);
-                addReply(c,e);
-                addReply(c,shared.crlf);
-            }
+            addReplyBulk(c,e);
         }
     }
 }
 
 static void hdelCommand(redisClient *c) {
-    robj *o = lookupKeyRead(c->db,c->argv[1]);
+    robj *o;
+    int deleted = 0;
 
-    if (o == NULL) {
-        addReply(c,shared.czero);
-        return;
+    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,o,REDIS_HASH)) return;
+
+    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+        robj *field = getDecodedObject(c->argv[2]);
+
+        o->ptr = zipmapDel((unsigned char*) o->ptr,
+            (unsigned char*) field->ptr,
+            sdslen(field->ptr), &deleted);
+        decrRefCount(field);
     } else {
-        int deleted = 0;
+        deleted = dictDelete((dict*)o->ptr,c->argv[2]) == DICT_OK;
+    }
+    if (deleted) server.dirty++;
+    addReply(c,deleted ? shared.cone : shared.czero);
+}
 
-        if (o->type != REDIS_HASH) {
-            addReply(c,shared.wrongtypeerr);
-            return;
+static void hlenCommand(redisClient *c) {
+    robj *o;
+    unsigned long len;
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,o,REDIS_HASH)) return;
+
+    len = (o->encoding == REDIS_ENCODING_ZIPMAP) ?
+            zipmapLen((unsigned char*)o->ptr) : dictSize((dict*)o->ptr);
+    addReplyUlong(c,len);
+}
+
+#define REDIS_GETALL_KEYS 1
+#define REDIS_GETALL_VALS 2
+static void genericHgetallCommand(redisClient *c, int flags) {
+    robj *o, *lenobj;
+    unsigned long count = 0;
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullmultibulk)) == NULL
+        || checkType(c,o,REDIS_HASH)) return;
+
+    lenobj = createObject(REDIS_STRING,NULL);
+    addReply(c,lenobj);
+    decrRefCount(lenobj);
+
+    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+        unsigned char *p = zipmapRewind(o->ptr);
+        unsigned char *field, *val;
+        unsigned int flen, vlen;
+
+        while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) {
+            robj *aux;
+
+            if (flags & REDIS_GETALL_KEYS) {
+                aux = createStringObject((char*)field,flen);
+                addReplyBulk(c,aux);
+                decrRefCount(aux);
+                count++;
+            }
+            if (flags & REDIS_GETALL_VALS) {
+                aux = createStringObject((char*)val,vlen);
+                addReplyBulk(c,aux);
+                decrRefCount(aux);
+                count++;
+            }
         }
+    } else {
+        dictIterator *di = dictGetIterator(o->ptr);
+        dictEntry *de;
 
-        if (o->encoding == REDIS_ENCODING_ZIPMAP) {
-            o->ptr = zipmapDel((unsigned char*) o->ptr,
-                (unsigned char*) c->argv[2]->ptr,
-                sdslen(c->argv[2]->ptr), &deleted);
-        } else {
-            deleted = dictDelete((dict*)o->ptr,c->argv[2]) == DICT_OK;
+        while((de = dictNext(di)) != NULL) {
+            robj *fieldobj = dictGetEntryKey(de);
+            robj *valobj = dictGetEntryVal(de);
+
+            if (flags & REDIS_GETALL_KEYS) {
+                addReplyBulk(c,fieldobj);
+                count++;
+            }
+            if (flags & REDIS_GETALL_VALS) {
+                addReplyBulk(c,valobj);
+                count++;
+            }
         }
-        addReply(c,deleted ? shared.cone : shared.czero);
+        dictReleaseIterator(di);
+    }
+    lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",count);
+}
+
+static void hkeysCommand(redisClient *c) {
+    genericHgetallCommand(c,REDIS_GETALL_KEYS);
+}
+
+static void hvalsCommand(redisClient *c) {
+    genericHgetallCommand(c,REDIS_GETALL_VALS);
+}
+
+static void hgetallCommand(redisClient *c) {
+    genericHgetallCommand(c,REDIS_GETALL_KEYS|REDIS_GETALL_VALS);
+}
+
+static void hexistsCommand(redisClient *c) {
+    robj *o;
+    int exists = 0;
+
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
+        checkType(c,o,REDIS_HASH)) return;
+
+    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+        robj *field;
+        unsigned char *zm = o->ptr;
+
+        field = getDecodedObject(c->argv[2]);
+        exists = zipmapExists(zm,field->ptr,sdslen(field->ptr));
+        decrRefCount(field);
+    } else {
+        exists = dictFind(o->ptr,c->argv[2]) != NULL;
     }
+    addReply(c,exists ? shared.cone : shared.czero);
 }
 
 static void convertToRealHash(robj *o) {
@@ -6345,11 +6394,7 @@ static void sortCommand(redisClient *c) {
             listNode *ln;
             listIter li;
 
-            if (!getop) {
-                addReplyBulkLen(c,vector[j].obj);
-                addReply(c,vector[j].obj);
-                addReply(c,shared.crlf);
-            }
+            if (!getop) addReplyBulk(c,vector[j].obj);
             listRewind(operations,&li);
             while((ln = listNext(&li))) {
                 redisSortOperation *sop = ln->value;
@@ -6360,9 +6405,7 @@ static void sortCommand(redisClient *c) {
                     if (!val || val->type != REDIS_STRING) {
                         addReply(c,shared.nullbulk);
                     } else {
-                        addReplyBulkLen(c,val);
-                        addReply(c,val);
-                        addReply(c,shared.crlf);
+                        addReplyBulk(c,val);
                     }
                 } else {
                     redisAssert(sop->type == REDIS_SORT_GET); /* always fails */
@@ -6449,7 +6492,7 @@ static sds genRedisInfoString(void) {
     time_t uptime = time(NULL)-server.stat_starttime;
     int j;
     char hmem[64];
-  
+
     bytesToHuman(hmem,zmalloc_used_memory());
     info = sdscatprintf(sdsempty(),
         "redis_version:%s\r\n"
@@ -6469,6 +6512,8 @@ static sds genRedisInfoString(void) {
         "bgrewriteaof_in_progress:%d\r\n"
         "total_connections_received:%lld\r\n"
         "total_commands_processed:%lld\r\n"
+        "hash_max_zipmap_entries:%ld\r\n"
+        "hash_max_zipmap_value:%ld\r\n"
         "vm_enabled:%d\r\n"
         "role:%s\r\n"
         ,REDIS_VERSION,
@@ -6488,6 +6533,8 @@ static sds genRedisInfoString(void) {
         server.bgrewritechildpid != -1,
         server.stat_numconnections,
         server.stat_numcommands,
+        server.hash_max_zipmap_entries,
+        server.hash_max_zipmap_value,
         server.vm_enabled != 0,
         server.masterhost == NULL ? "master" : "slave"
     );
@@ -6873,12 +6920,8 @@ static int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
     receiver = ln->value;
 
     addReplySds(receiver,sdsnew("*2\r\n"));
-    addReplyBulkLen(receiver,key);
-    addReply(receiver,key);
-    addReply(receiver,shared.crlf);
-    addReplyBulkLen(receiver,ele);
-    addReply(receiver,ele);
-    addReply(receiver,shared.crlf);
+    addReplyBulk(receiver,key);
+    addReplyBulk(receiver,ele);
     unblockClientWaitingData(receiver);
     return 1;
 }
@@ -6917,9 +6960,7 @@ static void blockingPopGenericCommand(redisClient *c, int where) {
                      * for us. If this souds like an hack to you it's just
                      * because it is... */
                     addReplySds(c,sdsnew("*2\r\n"));
-                    addReplyBulkLen(c,argv[1]);
-                    addReply(c,argv[1]);
-                    addReply(c,shared.crlf);
+                    addReplyBulk(c,argv[1]);
                     popGenericCommand(c,where);
 
                     /* Fix the client structure with the original stuff */
@@ -7783,7 +7824,7 @@ static int rewriteAppendOnlyFile(char *filename) {
                     dictReleaseIterator(di);
                 }
             } else {
-                redisAssert(0 != 0);
+                redisAssert(0);
             }
             /* Save the expire time */
             if (expiretime != -1) {
@@ -8283,6 +8324,38 @@ static double computeObjectSwappability(robj *o) {
             if (z) asize += sizeof(zskiplistNode)*dictSize(d);
         }
         break;
+    case REDIS_HASH:
+        if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+            unsigned char *p = zipmapRewind((unsigned char*)o->ptr);
+            unsigned int len = zipmapLen((unsigned char*)o->ptr);
+            unsigned int klen, vlen;
+            unsigned char *key, *val;
+
+            if ((p = zipmapNext(p,&key,&klen,&val,&vlen)) == NULL) {
+                klen = 0;
+                vlen = 0;
+            }
+            asize = len*(klen+vlen+3);
+        } else if (o->encoding == REDIS_ENCODING_HT) {
+            d = o->ptr;
+            asize = sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));
+            if (dictSize(d)) {
+                long elesize;
+                robj *ele;
+
+                de = dictGetRandomKey(d);
+                ele = dictGetEntryKey(de);
+                elesize = (ele->encoding == REDIS_ENCODING_RAW) ?
+                                (sizeof(*o)+sdslen(ele->ptr)) :
+                                sizeof(*o);
+                ele = dictGetEntryVal(de);
+                elesize = (ele->encoding == REDIS_ENCODING_RAW) ?
+                                (sizeof(*o)+sdslen(ele->ptr)) :
+                                sizeof(*o);
+                asize += (sizeof(struct dictEntry)+elesize)*dictSize(d);
+            }
+        }
+        break;
     }
     return (double)age*log(1+asize);
 }
@@ -8685,13 +8758,18 @@ static void *IOThreadEntryPoint(void *arg) {
 static void spawnIOThread(void) {
     pthread_t thread;
     sigset_t mask, omask;
+    int err;
 
     sigemptyset(&mask);
     sigaddset(&mask,SIGCHLD);
     sigaddset(&mask,SIGHUP);
     sigaddset(&mask,SIGPIPE);
     pthread_sigmask(SIG_SETMASK, &mask, &omask);
-    pthread_create(&thread,&server.io_threads_attr,IOThreadEntryPoint,NULL);
+    while ((err = pthread_create(&thread,&server.io_threads_attr,IOThreadEntryPoint,NULL)) != 0) {
+        redisLog(REDIS_WARNING,"Unable to spawn an I/O thread: %s",
+            strerror(err));
+        usleep(1000000);
+    }
     pthread_sigmask(SIG_SETMASK, &omask, NULL);
     server.io_active_threads++;
 }
@@ -8836,6 +8914,15 @@ static int waitForSwappedKey(redisClient *c, robj *key) {
     return 1;
 }
 
+/* Preload keys needed for the ZUNION and ZINTER commands. */
+static void zunionInterBlockClientOnSwappedKeys(redisClient *c) {
+    int i, num;
+    num = atoi(c->argv[2]->ptr);
+    for (i = 0; i < num; i++) {
+        waitForSwappedKey(c,c->argv[3+i]);
+    }
+}
+
 /* Is this client attempting to run a command against swapped keys?
  * If so, block it ASAP, load the keys in background, then resume it.
  *
@@ -8849,11 +8936,16 @@ static int waitForSwappedKey(redisClient *c, robj *key) {
 static int blockClientOnSwappedKeys(struct redisCommand *cmd, redisClient *c) {
     int j, last;
 
-    if (cmd->vm_firstkey == 0) return 0;
-    last = cmd->vm_lastkey;
-    if (last < 0) last = c->argc+last;
-    for (j = cmd->vm_firstkey; j <= last; j += cmd->vm_keystep)
-        waitForSwappedKey(c,c->argv[j]);
+    if (cmd->vm_preload_proc != NULL) {
+        cmd->vm_preload_proc(c);
+    } else {
+        if (cmd->vm_firstkey == 0) return 0;
+        last = cmd->vm_lastkey;
+        if (last < 0) last = c->argc+last;
+        for (j = cmd->vm_firstkey; j <= last; j += cmd->vm_keystep)
+            waitForSwappedKey(c,c->argv[j]);
+    }
+
     /* If the client was blocked for at least one key, mark it as blocked. */
     if (listLength(c->io_keys)) {
         c->flags |= REDIS_IO_WAIT;
@@ -9073,16 +9165,28 @@ static void daemonize(void) {
     }
 }
 
+static void version() {
+    printf("Redis server version %s\n", REDIS_VERSION);
+    exit(0);
+}
+
+static void usage() {
+    fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");
+    exit(1);
+}
+
 int main(int argc, char **argv) {
     time_t start;
 
     initServerConfig();
     if (argc == 2) {
+        if (strcmp(argv[1], "-v") == 0 ||
+            strcmp(argv[1], "--version") == 0) version();
+        if (strcmp(argv[1], "--help") == 0) usage();
         resetServerSaveParams();
         loadServerConfig(argv[1]);
-    } else if (argc > 2) {
-        fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");
-        exit(1);
+    } else if ((argc > 2)) {
+        usage();
     } else {
         redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'");
     }