]> git.saurik.com Git - redis.git/blobdiff - redis.c
fix for the LZF off-by-one bug added
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index 7c382af74993d9b33059a6e0f82e5958fdd7a334..1237a048d25f92221ed7528475756ebe2c8cb39d 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -341,6 +341,7 @@ static void typeCommand(redisClient *c);
 static void lsetCommand(redisClient *c);
 static void saddCommand(redisClient *c);
 static void sremCommand(redisClient *c);
+static void smoveCommand(redisClient *c);
 static void sismemberCommand(redisClient *c);
 static void scardCommand(redisClient *c);
 static void sinterCommand(redisClient *c);
@@ -356,6 +357,7 @@ static void infoCommand(redisClient *c);
 static void mgetCommand(redisClient *c);
 static void monitorCommand(redisClient *c);
 static void expireCommand(redisClient *c);
+static void getSetCommand(redisClient *c);
 
 /*================================= Globals ================================= */
 
@@ -382,6 +384,7 @@ static struct redisCommand cmdTable[] = {
     {"lrem",lremCommand,4,REDIS_CMD_BULK},
     {"sadd",saddCommand,3,REDIS_CMD_BULK},
     {"srem",sremCommand,3,REDIS_CMD_BULK},
+    {"smove",smoveCommand,4,REDIS_CMD_BULK},
     {"sismember",sismemberCommand,3,REDIS_CMD_BULK},
     {"scard",scardCommand,2,REDIS_CMD_INLINE},
     {"sinter",sinterCommand,-2,REDIS_CMD_INLINE},
@@ -391,6 +394,7 @@ static struct redisCommand cmdTable[] = {
     {"smembers",sinterCommand,2,REDIS_CMD_INLINE},
     {"incrby",incrbyCommand,3,REDIS_CMD_INLINE},
     {"decrby",decrbyCommand,3,REDIS_CMD_INLINE},
+    {"getset",getSetCommand,3,REDIS_CMD_BULK},
     {"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE},
     {"select",selectCommand,2,REDIS_CMD_INLINE},
     {"move",moveCommand,3,REDIS_CMD_INLINE},
@@ -875,13 +879,22 @@ static void initServer() {
 }
 
 /* Empty the whole database */
-static void emptyDb() {
+static long long emptyDb() {
     int j;
+    long long removed = 0;
 
     for (j = 0; j < server.dbnum; j++) {
+        removed += dictSize(server.db[j].dict);
         dictEmpty(server.db[j].dict);
         dictEmpty(server.db[j].expires);
     }
+    return removed;
+}
+
+static int yesnotoi(char *s) {
+    if (!strcasecmp(s,"yes")) return 1;
+    else if (!strcasecmp(s,"no")) return 0;
+    else return -1;
 }
 
 /* I agree, this is a very rudimental way to load a configuration...
@@ -915,44 +928,44 @@ static void loadServerConfig(char *filename) {
         sdstolower(argv[0]);
 
         /* Execute config directives */
-        if (!strcmp(argv[0],"timeout") && argc == 2) {
+        if (!strcasecmp(argv[0],"timeout") && argc == 2) {
             server.maxidletime = atoi(argv[1]);
             if (server.maxidletime < 1) {
                 err = "Invalid timeout value"; goto loaderr;
             }
-        } else if (!strcmp(argv[0],"port") && argc == 2) {
+        } else if (!strcasecmp(argv[0],"port") && argc == 2) {
             server.port = atoi(argv[1]);
             if (server.port < 1 || server.port > 65535) {
                 err = "Invalid port"; goto loaderr;
             }
-        } else if (!strcmp(argv[0],"bind") && argc == 2) {
+        } else if (!strcasecmp(argv[0],"bind") && argc == 2) {
             server.bindaddr = zstrdup(argv[1]);
-        } else if (!strcmp(argv[0],"save") && argc == 3) {
+        } else if (!strcasecmp(argv[0],"save") && argc == 3) {
             int seconds = atoi(argv[1]);
             int changes = atoi(argv[2]);
             if (seconds < 1 || changes < 0) {
                 err = "Invalid save parameters"; goto loaderr;
             }
             appendServerSaveParams(seconds,changes);
-        } else if (!strcmp(argv[0],"dir") && argc == 2) {
+        } else if (!strcasecmp(argv[0],"dir") && argc == 2) {
             if (chdir(argv[1]) == -1) {
                 redisLog(REDIS_WARNING,"Can't chdir to '%s': %s",
                     argv[1], strerror(errno));
                 exit(1);
             }
-        } else if (!strcmp(argv[0],"loglevel") && argc == 2) {
-            if (!strcmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG;
-            else if (!strcmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE;
-            else if (!strcmp(argv[1],"warning")) server.verbosity = REDIS_WARNING;
+        } else if (!strcasecmp(argv[0],"loglevel") && argc == 2) {
+            if (!strcasecmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG;
+            else if (!strcasecmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE;
+            else if (!strcasecmp(argv[1],"warning")) server.verbosity = REDIS_WARNING;
             else {
                 err = "Invalid log level. Must be one of debug, notice, warning";
                 goto loaderr;
             }
-        } else if (!strcmp(argv[0],"logfile") && argc == 2) {
+        } else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
             FILE *fp;
 
             server.logfile = zstrdup(argv[1]);
-            if (!strcmp(server.logfile,"stdout")) {
+            if (!strcasecmp(server.logfile,"stdout")) {
                 zfree(server.logfile);
                 server.logfile = NULL;
             }
@@ -967,40 +980,33 @@ static void loadServerConfig(char *filename) {
                 }
                 fclose(fp);
             }
-        } else if (!strcmp(argv[0],"databases") && argc == 2) {
+        } else if (!strcasecmp(argv[0],"databases") && argc == 2) {
             server.dbnum = atoi(argv[1]);
             if (server.dbnum < 1) {
                 err = "Invalid number of databases"; goto loaderr;
             }
-        } else if (!strcmp(argv[0],"slaveof") && argc == 3) {
+        } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
             server.masterhost = sdsnew(argv[1]);
             server.masterport = atoi(argv[2]);
             server.replstate = REDIS_REPL_CONNECT;
-        } else if (!strcmp(argv[0],"glueoutputbuf") && argc == 2) {
-            sdstolower(argv[1]);
-            if (!strcmp(argv[1],"yes")) server.glueoutputbuf = 1;
-            else if (!strcmp(argv[1],"no")) server.glueoutputbuf = 0;
-            else {
+        } else if (!strcasecmp(argv[0],"glueoutputbuf") && argc == 2) {
+            if ((server.glueoutputbuf = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
             }
-        } else if (!strcmp(argv[0],"shareobjects") && argc == 2) {
-            sdstolower(argv[1]);
-            if (!strcmp(argv[1],"yes")) server.shareobjects = 1;
-            else if (!strcmp(argv[1],"no")) server.shareobjects = 0;
-            else {
+        } else if (!strcasecmp(argv[0],"shareobjects") && argc == 2) {
+            if ((server.shareobjects = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
             }
-        } else if (!strcmp(argv[0],"daemonize") && argc == 2) {
-            sdstolower(argv[1]);
-            if (!strcmp(argv[1],"yes")) server.daemonize = 1;
-            else if (!strcmp(argv[1],"no")) server.daemonize = 0;
-            else {
+        } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
+            if ((server.daemonize = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
             }
-        } else if (!strcmp(argv[0],"requirepass") && argc == 2) {
+        } else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
           server.requirepass = zstrdup(argv[1]);
-        } else if (!strcmp(argv[0],"pidfile") && argc == 2) {
+        } else if (!strcasecmp(argv[0],"pidfile") && argc == 2) {
           server.pidfile = zstrdup(argv[1]);
+        } else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) {
+          server.dbfilename = zstrdup(argv[1]);
         } else {
             err = "Bad directive or wrong number of arguments"; goto loaderr;
         }
@@ -1136,7 +1142,7 @@ static void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask)
 static struct redisCommand *lookupCommand(char *name) {
     int j = 0;
     while(cmdTable[j].name != NULL) {
-        if (!strcmp(name,cmdTable[j].name)) return &cmdTable[j];
+        if (!strcasecmp(name,cmdTable[j].name)) return &cmdTable[j];
         j++;
     }
     return NULL;
@@ -1160,10 +1166,9 @@ static int processCommand(redisClient *c) {
     struct redisCommand *cmd;
     long long dirty;
 
-    sdstolower(c->argv[0]->ptr);
     /* The QUIT command is handled as a special case. Normal command
      * procs are unable to close the client connection safely */
-    if (!strcmp(c->argv[0]->ptr,"quit")) {
+    if (!strcasecmp(c->argv[0]->ptr,"quit")) {
         freeClient(c);
         return 0;
     }
@@ -1699,7 +1704,7 @@ static int rdbSaveLzfStringObject(FILE *fp, robj *obj) {
     /* We require at least four bytes compression for this to be worth it */
     outlen = sdslen(obj->ptr)-4;
     if (outlen <= 0) return 0;
-    if ((out = zmalloc(outlen)) == NULL) return 0;
+    if ((out = zmalloc(outlen+1)) == NULL) return 0;
     comprlen = lzf_compress(obj->ptr, sdslen(obj->ptr), out, outlen);
     if (comprlen == 0) {
         zfree(out);
@@ -1736,7 +1741,7 @@ static int rdbSaveStringObject(FILE *fp, robj *obj) {
 
     /* Try LZF compression - under 20 bytes it's unable to compress even
      * aaaaaaaaaaaaaaaaaa so skip it */
-    if (len > 20) {
+    if (1 && len > 20) {
         int retval;
 
         retval = rdbSaveLzfStringObject(fp,obj);
@@ -2172,6 +2177,18 @@ static void getCommand(redisClient *c) {
     }
 }
 
+static void getSetCommand(redisClient *c) {
+    getCommand(c);
+    if (dictAdd(c->db->dict,c->argv[1],c->argv[2]) == DICT_ERR) {
+        dictReplace(c->db->dict,c->argv[1],c->argv[2]);
+    } else {
+        incrRefCount(c->argv[1]);
+    }
+    incrRefCount(c->argv[2]);
+    server.dirty++;
+    removeExpire(c->db,c->argv[1]);
+}
+
 static void mgetCommand(redisClient *c) {
     int j;
   
@@ -2192,7 +2209,7 @@ static void mgetCommand(redisClient *c) {
     }
 }
 
-static void incrDecrCommand(redisClient *c, int incr) {
+static void incrDecrCommand(redisClient *c, long long incr) {
     long long value;
     int retval;
     robj *o;
@@ -2234,12 +2251,12 @@ static void decrCommand(redisClient *c) {
 }
 
 static void incrbyCommand(redisClient *c) {
-    int incr = atoi(c->argv[2]->ptr);
+    long long incr = strtoll(c->argv[2]->ptr, NULL, 10);
     incrDecrCommand(c,incr);
 }
 
 static void decrbyCommand(redisClient *c) {
-    int incr = atoi(c->argv[2]->ptr);
+    long long incr = strtoll(c->argv[2]->ptr, NULL, 10);
     incrDecrCommand(c,-incr);
 }
 
@@ -2375,8 +2392,9 @@ static void shutdownCommand(redisClient *c) {
     /* XXX: TODO kill the child if there is a bgsave in progress */
     if (rdbSave(server.dbfilename) == REDIS_OK) {
         if (server.daemonize) {
-          unlink(server.pidfile);
+            unlink(server.pidfile);
         }
+        redisLog(REDIS_WARNING,"%zu bytes used at exit",zmalloc_used_memory());
         redisLog(REDIS_WARNING,"Server exit now, bye bye...");
         exit(1);
     } else {
@@ -2804,6 +2822,41 @@ static void sremCommand(redisClient *c) {
     }
 }
 
+static void smoveCommand(redisClient *c) {
+    robj *srcset, *dstset;
+
+    srcset = lookupKeyWrite(c->db,c->argv[1]);
+    dstset = lookupKeyWrite(c->db,c->argv[2]);
+
+    /* If the source key does not exist return 0, if it's of the wrong type
+     * raise an error */
+    if (srcset == NULL || srcset->type != REDIS_SET) {
+        addReply(c, srcset ? shared.wrongtypeerr : shared.czero);
+        return;
+    }
+    /* Error if the destination key is not a set as well */
+    if (dstset && dstset->type != REDIS_SET) {
+        addReply(c,shared.wrongtypeerr);
+        return;
+    }
+    /* Remove the element from the source set */
+    if (dictDelete(srcset->ptr,c->argv[3]) == DICT_ERR) {
+        /* Key not found in the src set! return zero */
+        addReply(c,shared.czero);
+        return;
+    }
+    server.dirty++;
+    /* Add the element to the destination set */
+    if (!dstset) {
+        dstset = createSetObject();
+        dictAdd(c->db->dict,c->argv[2],dstset);
+        incrRefCount(c->argv[2]);
+    }
+    if (dictAdd(dstset->ptr,c->argv[3],NULL) == DICT_OK)
+        incrRefCount(c->argv[3]);
+    addReply(c,shared.cone);
+}
+
 static void sismemberCommand(redisClient *c) {
     robj *set;
 
@@ -3038,18 +3091,17 @@ static void sunionstoreCommand(redisClient *c) {
 }
 
 static void flushdbCommand(redisClient *c) {
+    server.dirty += dictSize(c->db->dict);
     dictEmpty(c->db->dict);
     dictEmpty(c->db->expires);
-    server.dirty++;
     addReply(c,shared.ok);
-    rdbSave(server.dbfilename);
 }
 
 static void flushallCommand(redisClient *c) {
-    emptyDb();
-    server.dirty++;
+    server.dirty += emptyDb();
     addReply(c,shared.ok);
     rdbSave(server.dbfilename);
+    server.dirty++;
 }
 
 redisSortOperation *createSortOperation(int type, robj *pattern) {
@@ -3341,6 +3393,7 @@ static void infoCommand(redisClient *c) {
         "connected_slaves:%d\r\n"
         "used_memory:%zu\r\n"
         "changes_since_last_save:%lld\r\n"
+        "bgsave_in_progress:%d\r\n"
         "last_save_time:%d\r\n"
         "total_connections_received:%lld\r\n"
         "total_commands_processed:%lld\r\n"
@@ -3351,6 +3404,7 @@ static void infoCommand(redisClient *c) {
         listLength(server.slaves),
         server.usedmemory,
         server.dirty,
+        server.bgsaveinprogress,
         server.lastsave,
         server.stat_numconnections,
         server.stat_numcommands,