]> git.saurik.com Git - redis.git/commitdiff
Merged Pietern patch for VM key args helper function. Fixed an obvious bug in the...
authorantirez <antirez@gmail.com>
Wed, 17 Mar 2010 16:14:07 +0000 (17:14 +0100)
committerantirez <antirez@gmail.com>
Wed, 17 Mar 2010 16:14:07 +0000 (17:14 +0100)
1  2 
redis-cli.c
redis.c

diff --combined redis-cli.c
index dbf6c9f96f77099796f3e3cd6019c78bdf03ac2c,03755efaca89a7af35d239c21bd539ad288496ae..5c1a2a1fb1273f56861a33fd959e27864c7206e1
@@@ -52,7 -52,6 +52,7 @@@ static struct config 
      long repeat;
      int dbnum;
      int interactive;
 +    char *authpw;
  } config;
  
  struct redisCommand {
@@@ -62,7 -61,6 +62,7 @@@
  };
  
  static struct redisCommand cmdTable[] = {
 +    {"auth",2,REDIS_CMD_INLINE},
      {"get",2,REDIS_CMD_INLINE},
      {"set",3,REDIS_CMD_BULK},
      {"setnx",3,REDIS_CMD_BULK},
      {"hkeys",2,REDIS_CMD_INLINE},
      {"hvals",2,REDIS_CMD_INLINE},
      {"hgetall",2,REDIS_CMD_INLINE},
 +    {"hexists",3,REDIS_CMD_BULK},
      {NULL,0,0}
  };
  
@@@ -307,32 -304,6 +307,27 @@@ static int selectDb(int fd) 
      return 0;
  }
  
-     if (config.authpw != "")
-     {
 +static int cliLogin(int fd)
 +{
 +    int retval = 1;
 +    sds cmd;
 +    char type;
-         {
++    if (config.authpw[0] != '\0') {
 +        cmd = sdsempty();
 +        cmd = sdscatprintf(cmd,"AUTH %s\r\n",config.authpw);
 +        anetWrite(fd,cmd,sdslen(cmd));
 +        anetRead(fd,&type,1);
 +        if (type == '+') 
-         }
 +            retval = 0;
-         {
 +        int ret2 = cliReadSingleLineReply(fd,1);
 +        if (ret2)
-         }
 +            close(fd);
 +    } else {
 +        retval = 0;
 +    }
 +    return retval;
 +}
 +
  static int cliSendCommand(int argc, char **argv) {
      struct redisCommand *rc = lookupCommand(argv[0]);
      int fd, j, retval = 0;
          return 1;
      }
  
 +    /* Login if necessary */
 +    retval = cliLogin(fd);
 +    if (retval) {
 +        fprintf(stderr,"Authentication failed\n");
 +        return 1;
 +    }
 +
      while(config.repeat--) {
          /* Build the command to send */
          cmd = sdsempty();
@@@ -433,9 -397,6 +428,9 @@@ static int parseOptions(int argc, char 
          } else if (!strcmp(argv[i],"-n") && !lastarg) {
              config.dbnum = atoi(argv[i+1]);
              i++;
 +        } else if (!strcmp(argv[i],"-a") && !lastarg) {
 +            config.authpw = argv[i+1];
 +            i++;
          } else if (!strcmp(argv[i],"-i")) {
              config.interactive = 1;
          } else {
@@@ -463,8 -424,8 +458,8 @@@ static sds readArgFromStdin(void) 
  }
  
  static void usage() {
 -    fprintf(stderr, "usage: redis-cli [-h host] [-p port] [-r repeat_times] [-n db_num] [-i] cmd arg1 arg2 arg3 ... argN\n");
 -    fprintf(stderr, "usage: echo \"argN\" | redis-cli [-h host] [-p port] [-r repeat_times] [-n db_num] cmd arg1 arg2 ... arg(N-1)\n");
 +    fprintf(stderr, "usage: redis-cli [-h host] [-p port] [-a authpw] [-r repeat_times] [-n db_num] [-i] cmd arg1 arg2 arg3 ... argN\n");
 +    fprintf(stderr, "usage: echo \"argN\" | redis-cli [-h host] [-a authpw] [-p port] [-r repeat_times] [-n db_num] cmd arg1 arg2 ... arg(N-1)\n");
      fprintf(stderr, "\nIf a pipe from standard input is detected this data is used as last argument.\n\n");
      fprintf(stderr, "example: cat /etc/passwd | redis-cli set my_passwd\n");
      fprintf(stderr, "example: redis-cli get my_passwd\n");
diff --combined redis.c
index 8faa1edb4ac221e8306bdb78b52eb2b45295cfbf,5b9e73c3ddad6840e485b3ba40f8eb20b2409100..fe84898fe239671396e5b9c8e58b7a3b936ee62f
+++ b/redis.c
@@@ -443,6 -443,10 +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 +591,7 @@@ static robj *vmReadObjectFromSwap(off_
  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);
@@@ -689,109 -694,107 +694,109 @@@ static void zinterCommand(redisClient *
  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_BULK,1,1,1},
-     {"zrevrank",zrevrankCommand,3,REDIS_CMD_BULK,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},
-     {"hlen",hlenCommand,2,REDIS_CMD_INLINE,1,1,1},
-     {"hkeys",hkeysCommand,2,REDIS_CMD_INLINE,1,1,1},
-     {"hvals",hvalsCommand,2,REDIS_CMD_INLINE,1,1,1},
-     {"hgetall",hgetallCommand,2,REDIS_CMD_INLINE,1,1,1},
-     {"hexists",hexistsCommand,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}
  };
  
  /*============================ Utility functions ============================ */
@@@ -5389,26 -5392,8 +5394,26 @@@ static int qsortCompareZsetopsrcByCardi
      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;
  
      /* 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);
          }
      }
  
 +    /* 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;
                      }
                  /* 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);
                      }
                  }
  
@@@ -5881,7 -5854,7 +5886,7 @@@ static void hsetCommand(redisClient *c
          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 (dictAdd(o->ptr,c->argv[2],c->argv[3]) == DICT_OK) {
 +        if (dictReplace(o->ptr,c->argv[2],c->argv[3])) {
              incrRefCount(c->argv[2]);
          } else {
              update = 1;
@@@ -6027,26 -6000,6 +6032,26 @@@ static void hgetallCommand(redisClient 
      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) {
      unsigned char *key, *val, *p, *zm = o->ptr;
      unsigned int klen, vlen;
@@@ -8858,6 -8811,15 +8863,15 @@@ static int waitForSwappedKey(redisClien
      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.
   *
  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;