]> git.saurik.com Git - redis.git/blobdiff - redis.c
Automagically reconnection of RubyRedis
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index 89812107edfc33529251b170065aedbc1aa2a6eb..fbd1bc3df1807678cc297dcdc8ec3958f4061c84 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -56,7 +56,8 @@
 #include "dict.h"   /* Hash tables */
 #include "adlist.h" /* Linked lists */
 #include "zmalloc.h" /* total memory usage aware version of malloc/free */
 #include "dict.h"   /* Hash tables */
 #include "adlist.h" /* Linked lists */
 #include "zmalloc.h" /* total memory usage aware version of malloc/free */
-#include "lzf.h"
+#include "lzf.h"    /* LZF compression library */
+#include "pqsort.h" /* Partial qsort for SORT+LIMIT */
 
 /* Error codes */
 #define REDIS_OK                0
 
 /* Error codes */
 #define REDIS_OK                0
@@ -67,7 +68,7 @@
 #define REDIS_MAXIDLETIME       (60*5)  /* default client timeout */
 #define REDIS_IOBUF_LEN         1024
 #define REDIS_LOADBUF_LEN       1024
 #define REDIS_MAXIDLETIME       (60*5)  /* default client timeout */
 #define REDIS_IOBUF_LEN         1024
 #define REDIS_LOADBUF_LEN       1024
-#define REDIS_MAX_ARGS          16
+#define REDIS_STATIC_ARGS       4
 #define REDIS_DEFAULT_DBNUM     16
 #define REDIS_CONFIGLINE_MAX    1024
 #define REDIS_OBJFREELIST_MAX   1000000 /* Max number of objects to cache */
 #define REDIS_DEFAULT_DBNUM     16
 #define REDIS_CONFIGLINE_MAX    1024
 #define REDIS_OBJFREELIST_MAX   1000000 /* Max number of objects to cache */
@@ -183,7 +184,7 @@ typedef struct redisClient {
     redisDb *db;
     int dictid;
     sds querybuf;
     redisDb *db;
     int dictid;
     sds querybuf;
-    robj *argv[REDIS_MAX_ARGS];
+    robj **argv;
     int argc;
     int bulklen;            /* bulk read len. -1 if not in bulk read mode */
     list *reply;
     int argc;
     int bulklen;            /* bulk read len. -1 if not in bulk read mode */
     list *reply;
@@ -348,6 +349,8 @@ static void sinterCommand(redisClient *c);
 static void sinterstoreCommand(redisClient *c);
 static void sunionCommand(redisClient *c);
 static void sunionstoreCommand(redisClient *c);
 static void sinterstoreCommand(redisClient *c);
 static void sunionCommand(redisClient *c);
 static void sunionstoreCommand(redisClient *c);
+static void sdiffCommand(redisClient *c);
+static void sdiffstoreCommand(redisClient *c);
 static void syncCommand(redisClient *c);
 static void flushdbCommand(redisClient *c);
 static void flushallCommand(redisClient *c);
 static void syncCommand(redisClient *c);
 static void flushdbCommand(redisClient *c);
 static void flushallCommand(redisClient *c);
@@ -367,7 +370,7 @@ static struct redisCommand cmdTable[] = {
     {"get",getCommand,2,REDIS_CMD_INLINE},
     {"set",setCommand,3,REDIS_CMD_BULK},
     {"setnx",setnxCommand,3,REDIS_CMD_BULK},
     {"get",getCommand,2,REDIS_CMD_INLINE},
     {"set",setCommand,3,REDIS_CMD_BULK},
     {"setnx",setnxCommand,3,REDIS_CMD_BULK},
-    {"del",delCommand,2,REDIS_CMD_INLINE},
+    {"del",delCommand,-2,REDIS_CMD_INLINE},
     {"exists",existsCommand,2,REDIS_CMD_INLINE},
     {"incr",incrCommand,2,REDIS_CMD_INLINE},
     {"decr",decrCommand,2,REDIS_CMD_INLINE},
     {"exists",existsCommand,2,REDIS_CMD_INLINE},
     {"incr",incrCommand,2,REDIS_CMD_INLINE},
     {"decr",decrCommand,2,REDIS_CMD_INLINE},
@@ -391,6 +394,8 @@ static struct redisCommand cmdTable[] = {
     {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE},
     {"sunion",sunionCommand,-2,REDIS_CMD_INLINE},
     {"sunionstore",sunionstoreCommand,-3,REDIS_CMD_INLINE},
     {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE},
     {"sunion",sunionCommand,-2,REDIS_CMD_INLINE},
     {"sunionstore",sunionstoreCommand,-3,REDIS_CMD_INLINE},
+    {"sdiff",sdiffCommand,-2,REDIS_CMD_INLINE},
+    {"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_INLINE},
     {"smembers",sinterCommand,2,REDIS_CMD_INLINE},
     {"incrby",incrbyCommand,3,REDIS_CMD_INLINE},
     {"decrby",decrbyCommand,3,REDIS_CMD_INLINE},
     {"smembers",sinterCommand,2,REDIS_CMD_INLINE},
     {"incrby",incrbyCommand,3,REDIS_CMD_INLINE},
     {"decrby",decrbyCommand,3,REDIS_CMD_INLINE},
@@ -556,7 +561,12 @@ void redisLog(int level, const char *fmt, ...)
     va_start(ap, fmt);
     if (level >= server.verbosity) {
         char *c = ".-*";
     va_start(ap, fmt);
     if (level >= server.verbosity) {
         char *c = ".-*";
-        fprintf(fp,"%c ",c[level]);
+        char buf[64];
+        time_t now;
+
+        now = time(NULL);
+        strftime(buf,64,"%d %b %H:%M:%S",gmtime(&now));
+        fprintf(fp,"%s %c ",buf,c[level]);
         vfprintf(fp, fmt, ap);
         fprintf(fp,"\n");
         fflush(fp);
         vfprintf(fp, fmt, ap);
         fprintf(fp,"\n");
         fflush(fp);
@@ -711,7 +721,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
     }
 
     /* Close connections of timedout clients */
     }
 
     /* Close connections of timedout clients */
-    if (!(loops % 10))
+    if (server.maxidletime && !(loops % 10))
         closeTimedoutClients();
 
     /* Check if a background saving in progress terminated */
         closeTimedoutClients();
 
     /* Check if a background saving in progress terminated */
@@ -950,7 +960,7 @@ static void loadServerConfig(char *filename) {
         /* Execute config directives */
         if (!strcasecmp(argv[0],"timeout") && argc == 2) {
             server.maxidletime = atoi(argv[1]);
         /* Execute config directives */
         if (!strcasecmp(argv[0],"timeout") && argc == 2) {
             server.maxidletime = atoi(argv[1]);
-            if (server.maxidletime < 1) {
+            if (server.maxidletime < 0) {
                 err = "Invalid timeout value"; goto loaderr;
             }
         } else if (!strcasecmp(argv[0],"port") && argc == 2) {
                 err = "Invalid timeout value"; goto loaderr;
             }
         } else if (!strcasecmp(argv[0],"port") && argc == 2) {
@@ -1078,6 +1088,7 @@ static void freeClient(redisClient *c) {
         server.master = NULL;
         server.replstate = REDIS_REPL_CONNECT;
     }
         server.master = NULL;
         server.replstate = REDIS_REPL_CONNECT;
     }
+    zfree(c->argv);
     zfree(c);
 }
 
     zfree(c);
 }
 
@@ -1257,8 +1268,17 @@ static int processCommand(redisClient *c) {
 
 static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int dictid, robj **argv, int argc) {
     listNode *ln;
 
 static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int dictid, robj **argv, int argc) {
     listNode *ln;
-    robj *outv[REDIS_MAX_ARGS*4]; /* enough room for args, spaces, newlines */
     int outc = 0, j;
     int outc = 0, j;
+    robj **outv;
+    /* (args*2)+1 is enough room for args, spaces, newlines */
+    robj *static_outv[REDIS_STATIC_ARGS*2+1];
+
+    if (argc <= REDIS_STATIC_ARGS) {
+        outv = static_outv;
+    } else {
+        outv = zmalloc(sizeof(robj*)*(argc*2+1));
+        if (!outv) oom("replicationFeedSlaves");
+    }
     
     for (j = 0; j < argc; j++) {
         if (j != 0) outv[outc++] = shared.space;
     
     for (j = 0; j < argc; j++) {
         if (j != 0) outv[outc++] = shared.space;
@@ -1312,6 +1332,7 @@ static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int di
         for (j = 0; j < outc; j++) addReply(slave,outv[j]);
     }
     for (j = 0; j < outc; j++) decrRefCount(outv[j]);
         for (j = 0; j < outc; j++) addReply(slave,outv[j]);
     }
     for (j = 0; j < outc; j++) decrRefCount(outv[j]);
+    if (outv != static_outv) zfree(outv);
 }
 
 static void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
 }
 
 static void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
@@ -1369,9 +1390,14 @@ again:
                 return;
             }
             argv = sdssplitlen(query,sdslen(query)," ",1,&argc);
                 return;
             }
             argv = sdssplitlen(query,sdslen(query)," ",1,&argc);
-            sdsfree(query);
             if (argv == NULL) oom("sdssplitlen");
             if (argv == NULL) oom("sdssplitlen");
-            for (j = 0; j < argc && j < REDIS_MAX_ARGS; j++) {
+            sdsfree(query);
+
+            if (c->argv) zfree(c->argv);
+            c->argv = zmalloc(sizeof(robj*)*argc);
+            if (c->argv == NULL) oom("allocating arguments list for client");
+
+            for (j = 0; j < argc; j++) {
                 if (sdslen(argv[j])) {
                     c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);
                     c->argc++;
                 if (sdslen(argv[j])) {
                     c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);
                     c->argc++;
@@ -1430,6 +1456,7 @@ static redisClient *createClient(int fd) {
     c->fd = fd;
     c->querybuf = sdsempty();
     c->argc = 0;
     c->fd = fd;
     c->querybuf = sdsempty();
     c->argc = 0;
+    c->argv = NULL;
     c->bulklen = -1;
     c->sentlen = 0;
     c->flags = 0;
     c->bulklen = -1;
     c->sentlen = 0;
     c->flags = 0;
@@ -1995,6 +2022,7 @@ static robj *rdbLoadLzfStringObject(FILE*fp, int rdbver) {
     if ((val = sdsnewlen(NULL,len)) == NULL) goto err;
     if (fread(c,clen,1,fp) == 0) goto err;
     if (lzf_decompress(c,clen,val,len) == 0) goto err;
     if ((val = sdsnewlen(NULL,len)) == NULL) goto err;
     if (fread(c,clen,1,fp) == 0) goto err;
     if (lzf_decompress(c,clen,val,len) == 0) goto err;
+    zfree(c);
     return createObject(REDIS_STRING,val);
 err:
     zfree(c);
     return createObject(REDIS_STRING,val);
 err:
     zfree(c);
@@ -2288,11 +2316,24 @@ static void decrbyCommand(redisClient *c) {
 /* ========================= Type agnostic commands ========================= */
 
 static void delCommand(redisClient *c) {
 /* ========================= Type agnostic commands ========================= */
 
 static void delCommand(redisClient *c) {
-    if (deleteKey(c->db,c->argv[1])) {
-        server.dirty++;
-        addReply(c,shared.cone);
-    } else {
+    int deleted = 0, j;
+
+    for (j = 1; j < c->argc; j++) {
+        if (deleteKey(c->db,c->argv[j])) {
+            server.dirty++;
+            deleted++;
+        }
+    }
+    switch(deleted) {
+    case 0:
         addReply(c,shared.czero);
         addReply(c,shared.czero);
+        break;
+    case 1:
+        addReply(c,shared.cone);
+        break;
+    default:
+        addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",deleted));
+        break;
     }
 }
 
     }
 }
 
@@ -2973,9 +3014,6 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
         /* If we have a target key where to store the resulting set
          * create this key with an empty set inside */
         dstset = createSetObject();
         /* If we have a target key where to store the resulting set
          * create this key with an empty set inside */
         dstset = createSetObject();
-        deleteKey(c->db,dstkey);
-        dictAdd(c->db->dict,dstkey,dstset);
-        incrRefCount(dstkey);
     }
 
     /* Iterate all the elements of the first (smallest) set, and test
     }
 
     /* Iterate all the elements of the first (smallest) set, and test
@@ -3004,6 +3042,13 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
     }
     dictReleaseIterator(di);
 
     }
     dictReleaseIterator(di);
 
+    if (dstkey) {
+        /* Store the resulting set into the target */
+        deleteKey(c->db,dstkey);
+        dictAdd(c->db->dict,dstkey,dstset);
+        incrRefCount(dstkey);
+    }
+
     if (!dstkey) {
         lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n",cardinality);
     } else {
     if (!dstkey) {
         lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n",cardinality);
     } else {
@@ -3021,14 +3066,17 @@ static void sinterstoreCommand(redisClient *c) {
     sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);
 }
 
     sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);
 }
 
-static void sunionGenericCommand(redisClient *c, robj **setskeys, int setsnum, robj *dstkey) {
+#define REDIS_OP_UNION 0
+#define REDIS_OP_DIFF 1
+
+static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnum, robj *dstkey, int op) {
     dict **dv = zmalloc(sizeof(dict*)*setsnum);
     dictIterator *di;
     dictEntry *de;
     dict **dv = zmalloc(sizeof(dict*)*setsnum);
     dictIterator *di;
     dictEntry *de;
-    robj *lenobj = NULL, *dstset = NULL;
+    robj *dstset = NULL;
     int j, cardinality = 0;
 
     int j, cardinality = 0;
 
-    if (!dv) oom("sunionCommand");
+    if (!dv) oom("sunionDiffGenericCommand");
     for (j = 0; j < setsnum; j++) {
         robj *setobj;
 
     for (j = 0; j < setsnum; j++) {
         robj *setobj;
 
@@ -3052,27 +3100,10 @@ static void sunionGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
      * this set object will be the resulting object to set into the target key*/
     dstset = createSetObject();
 
      * this set object will be the resulting object to set into the target key*/
     dstset = createSetObject();
 
-    /* The first thing we should output is the total number of elements...
-     * since this is a multi-bulk write, but at this stage we don't know
-     * the intersection set size, so we use a trick, append an empty object
-     * to the output list and save the pointer to later modify it with the
-     * right length */
-    if (!dstkey) {
-        lenobj = createObject(REDIS_STRING,NULL);
-        addReply(c,lenobj);
-        decrRefCount(lenobj);
-    } else {
-        /* If we have a target key where to store the resulting set
-         * create this key with an empty set inside */
-        deleteKey(c->db,dstkey);
-        dictAdd(c->db->dict,dstkey,dstset);
-        incrRefCount(dstkey);
-        server.dirty++;
-    }
-
     /* Iterate all the elements of all the sets, add every element a single
      * time to the result set */
     for (j = 0; j < setsnum; j++) {
     /* Iterate all the elements of all the sets, add every element a single
      * time to the result set */
     for (j = 0; j < setsnum; j++) {
+        if (op == REDIS_OP_DIFF && j == 0 && !dv[j]) break; /* result set is empty */
         if (!dv[j]) continue; /* non existing keys are like empty sets */
 
         di = dictGetIterator(dv[j]);
         if (!dv[j]) continue; /* non existing keys are like empty sets */
 
         di = dictGetIterator(dv[j]);
@@ -3083,22 +3114,48 @@ static void sunionGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
 
             /* dictAdd will not add the same element multiple times */
             ele = dictGetEntryKey(de);
 
             /* dictAdd will not add the same element multiple times */
             ele = dictGetEntryKey(de);
-            if (dictAdd(dstset->ptr,ele,NULL) == DICT_OK) {
-                incrRefCount(ele);
-                if (!dstkey) {
-                    addReplySds(c,sdscatprintf(sdsempty(),
-                            "$%d\r\n",sdslen(ele->ptr)));
-                    addReply(c,ele);
-                    addReply(c,shared.crlf);
+            if (op == REDIS_OP_UNION || j == 0) {
+                if (dictAdd(dstset->ptr,ele,NULL) == DICT_OK) {
+                    incrRefCount(ele);
                     cardinality++;
                 }
                     cardinality++;
                 }
+            } else if (op == REDIS_OP_DIFF) {
+                if (dictDelete(dstset->ptr,ele) == DICT_OK) {
+                    cardinality--;
+                }
             }
         }
         dictReleaseIterator(di);
             }
         }
         dictReleaseIterator(di);
+
+        if (op == REDIS_OP_DIFF && cardinality == 0) break; /* result set is empty */
     }
 
     }
 
+    /* Output the content of the resulting set, if not in STORE mode */
+    if (!dstkey) {
+        addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",cardinality));
+        di = dictGetIterator(dstset->ptr);
+        if (!di) oom("dictGetIterator");
+        while((de = dictNext(di)) != NULL) {
+            robj *ele;
+
+            ele = dictGetEntryKey(de);
+            addReplySds(c,sdscatprintf(sdsempty(),
+                    "$%d\r\n",sdslen(ele->ptr)));
+            addReply(c,ele);
+            addReply(c,shared.crlf);
+        }
+        dictReleaseIterator(di);
+    } else {
+        /* If we have a target key where to store the resulting set
+         * create this key with the result set inside */
+        deleteKey(c->db,dstkey);
+        dictAdd(c->db->dict,dstkey,dstset);
+        incrRefCount(dstkey);
+        server.dirty++;
+    }
+
+    /* Cleanup */
     if (!dstkey) {
     if (!dstkey) {
-        lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n",cardinality);
         decrRefCount(dstset);
     } else {
         addReply(c,shared.ok);
         decrRefCount(dstset);
     } else {
         addReply(c,shared.ok);
@@ -3108,11 +3165,19 @@ static void sunionGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
 }
 
 static void sunionCommand(redisClient *c) {
 }
 
 static void sunionCommand(redisClient *c) {
-    sunionGenericCommand(c,c->argv+1,c->argc-1,NULL);
+    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_UNION);
 }
 
 static void sunionstoreCommand(redisClient *c) {
 }
 
 static void sunionstoreCommand(redisClient *c) {
-    sunionGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);
+    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_UNION);
+}
+
+static void sdiffCommand(redisClient *c) {
+    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_DIFF);
+}
+
+static void sdiffstoreCommand(redisClient *c) {
+    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_DIFF);
 }
 
 static void flushdbCommand(redisClient *c) {
 }
 
 static void flushdbCommand(redisClient *c) {
@@ -3362,7 +3427,10 @@ static void sortCommand(redisClient *c) {
         server.sort_desc = desc;
         server.sort_alpha = alpha;
         server.sort_bypattern = sortby ? 1 : 0;
         server.sort_desc = desc;
         server.sort_alpha = alpha;
         server.sort_bypattern = sortby ? 1 : 0;
-        qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare);
+        if (sortby && (start != 0 || end != vectorlen-1))
+            pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end);
+        else
+            qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare);
     }
 
     /* Send command output to the output buffer, performing the specified
     }
 
     /* Send command output to the output buffer, performing the specified