X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/f1017b3fb318d8524210ce4d599a0b083036dee9..2c65cbc9e3aa3ff189de3bfe207f4ee8f75edecb:/redis.c diff --git a/redis.c b/redis.c index f36e3b08..31402663 100644 --- a/redis.c +++ b/redis.c @@ -344,6 +344,7 @@ static int processCommand(redisClient *c); static void setupSigSegvAction(void); static void rdbRemoveTempFile(pid_t childpid); static size_t stringObjectLen(robj *o); +static void processInputBuffer(redisClient *c); static void authCommand(redisClient *c); static void pingCommand(redisClient *c); @@ -384,6 +385,7 @@ static void smoveCommand(redisClient *c); static void sismemberCommand(redisClient *c); static void scardCommand(redisClient *c); static void spopCommand(redisClient *c); +static void srandmemberCommand(redisClient *c); static void sinterCommand(redisClient *c); static void sinterstoreCommand(redisClient *c); static void sunionCommand(redisClient *c); @@ -399,10 +401,13 @@ 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); +static void getsetCommand(redisClient *c); static void ttlCommand(redisClient *c); static void slaveofCommand(redisClient *c); static void debugCommand(redisClient *c); +static void msetCommand(redisClient *c); +static void msetnxCommand(redisClient *c); + /*================================= Globals ================================= */ /* Global vars */ @@ -432,6 +437,7 @@ static struct redisCommand cmdTable[] = { {"sismember",sismemberCommand,3,REDIS_CMD_BULK}, {"scard",scardCommand,2,REDIS_CMD_INLINE}, {"spop",spopCommand,2,REDIS_CMD_INLINE}, + {"srandmember",srandmemberCommand,2,REDIS_CMD_INLINE}, {"sinter",sinterCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, {"sunion",sunionCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, @@ -441,7 +447,9 @@ static struct redisCommand cmdTable[] = { {"smembers",sinterCommand,2,REDIS_CMD_INLINE}, {"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, {"decrby",decrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, - {"getset",getSetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM}, + {"getset",getsetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM}, + {"mset",msetCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM}, + {"msetnx",msetnxCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM}, {"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE}, {"select",selectCommand,2,REDIS_CMD_INLINE}, {"move",moveCommand,3,REDIS_CMD_INLINE}, @@ -1423,7 +1431,10 @@ static int processCommand(redisClient *c) { c->argc--; c->bulklen = bulklen+2; /* add two bytes for CR+LF */ /* It is possible that the bulk read is already in the - * buffer. Check this condition and handle it accordingly */ + * buffer. Check this condition and handle it accordingly. + * This is just a fast path, alternative to call processInputBuffer(). + * It's a good idea since the code is small and this condition + * happens most of the times. */ if ((signed)sdslen(c->querybuf) >= c->bulklen) { c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2); c->argc++; @@ -1537,34 +1548,7 @@ static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int di if (outv != static_outv) zfree(outv); } -static void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { - redisClient *c = (redisClient*) privdata; - char buf[REDIS_IOBUF_LEN]; - int nread; - REDIS_NOTUSED(el); - REDIS_NOTUSED(mask); - - nread = read(fd, buf, REDIS_IOBUF_LEN); - if (nread == -1) { - if (errno == EAGAIN) { - nread = 0; - } else { - redisLog(REDIS_DEBUG, "Reading from client: %s",strerror(errno)); - freeClient(c); - return; - } - } else if (nread == 0) { - redisLog(REDIS_DEBUG, "Client closed connection"); - freeClient(c); - return; - } - if (nread) { - c->querybuf = sdscatlen(c->querybuf, buf, nread); - c->lastinteraction = time(NULL); - } else { - return; - } - +static void processInputBuffer(redisClient *c) { again: if (c->bulklen == -1) { /* Read the first line of the query */ @@ -1631,12 +1615,45 @@ again: c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2); c->argc++; c->querybuf = sdsrange(c->querybuf,c->bulklen,-1); - processCommand(c); + /* Process the command. If the client is still valid after + * the processing and there is more data in the buffer + * try to parse it. */ + if (processCommand(c) && sdslen(c->querybuf)) goto again; return; } } } +static void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { + redisClient *c = (redisClient*) privdata; + char buf[REDIS_IOBUF_LEN]; + int nread; + REDIS_NOTUSED(el); + REDIS_NOTUSED(mask); + + nread = read(fd, buf, REDIS_IOBUF_LEN); + if (nread == -1) { + if (errno == EAGAIN) { + nread = 0; + } else { + redisLog(REDIS_DEBUG, "Reading from client: %s",strerror(errno)); + freeClient(c); + return; + } + } else if (nread == 0) { + redisLog(REDIS_DEBUG, "Client closed connection"); + freeClient(c); + return; + } + if (nread) { + c->querybuf = sdscatlen(c->querybuf, buf, nread); + c->lastinteraction = time(NULL); + } else { + return; + } + processInputBuffer(c); +} + static int selectDb(redisClient *c, int id) { if (id < 0 || id >= server.dbnum) return REDIS_ERR; @@ -1921,7 +1938,7 @@ static robj *tryObjectSharing(robj *o) { * * If so, the function returns REDIS_OK and *longval is set to the value * of the number. Otherwise REDIS_ERR is returned */ -static int isStringRepresentableAsLong(char *s, long *longval) { +static int isStringRepresentableAsLong(sds s, long *longval) { char buf[32], *endptr; long value; int slen; @@ -1932,7 +1949,7 @@ static int isStringRepresentableAsLong(char *s, long *longval) { /* If the number converted back into a string is not identical * then it's not possible to encode the string as integer */ - if (strlen(buf) != (unsigned)slen || memcmp(buf,s,slen)) return REDIS_ERR; + if (sdslen(s) != (unsigned)slen || memcmp(buf,s,slen)) return REDIS_ERR; if (longval) *longval = value; return REDIS_OK; } @@ -2596,7 +2613,7 @@ static void getCommand(redisClient *c) { } } -static void getSetCommand(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]); @@ -3372,6 +3389,31 @@ static void spopCommand(redisClient *c) { } } +static void srandmemberCommand(redisClient *c) { + robj *set; + dictEntry *de; + + set = lookupKeyRead(c->db,c->argv[1]); + if (set == 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); + + addReplyBulkLen(c,ele); + addReply(c,ele); + addReply(c,shared.crlf); + } + } +} + static int qsortCompareSetsByCardinality(const void *s1, const void *s2) { dict **d1 = (void*) s1, **d2 = (void*) s2; @@ -4094,6 +4136,49 @@ static void ttlCommand(redisClient *c) { addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",ttl)); } +static void msetGenericCommand(redisClient *c, int nx) { + int j; + + if ((c->argc % 2) == 0) { + addReplySds(c,sdsnew("-ERR wrong number of arguments\r\n")); + return; + } + /* Handle the NX flag. The MSETNX semantic is to return zero and don't + * set nothing at all if at least one already key exists. */ + if (nx) { + for (j = 1; j < c->argc; j += 2) { + if (dictFind(c->db->dict,c->argv[j]) != NULL) { + addReply(c, shared.czero); + return; + } + } + } + + for (j = 1; j < c->argc; j += 2) { + int retval; + + retval = dictAdd(c->db->dict,c->argv[j],c->argv[j+1]); + if (retval == DICT_ERR) { + dictReplace(c->db->dict,c->argv[j],c->argv[j+1]); + incrRefCount(c->argv[j+1]); + } else { + incrRefCount(c->argv[j]); + incrRefCount(c->argv[j+1]); + } + removeExpire(c->db,c->argv[j]); + } + server.dirty += (c->argc-1)/2; + addReply(c, nx ? shared.cone : shared.ok); +} + +static void msetCommand(redisClient *c) { + msetGenericCommand(c,0); +} + +static void msetnxCommand(redisClient *c) { + msetGenericCommand(c,1); +} + /* =============================== Replication ============================= */ static int syncWrite(int fd, char *ptr, ssize_t size, int timeout) { @@ -4571,6 +4656,7 @@ static struct redisFunctionSym symsTable[] = { {"sismemberCommand", (unsigned long)sismemberCommand}, {"scardCommand", (unsigned long)scardCommand}, {"spopCommand", (unsigned long)spopCommand}, +{"srandmemberCommand", (unsigned long)srandmemberCommand}, {"sinterCommand", (unsigned long)sinterCommand}, {"sinterstoreCommand", (unsigned long)sinterstoreCommand}, {"sunionCommand", (unsigned long)sunionCommand}, @@ -4586,7 +4672,7 @@ static struct redisFunctionSym symsTable[] = { {"mgetCommand", (unsigned long)mgetCommand}, {"monitorCommand", (unsigned long)monitorCommand}, {"expireCommand", (unsigned long)expireCommand}, -{"getSetCommand", (unsigned long)getSetCommand}, +{"getsetCommand", (unsigned long)getsetCommand}, {"ttlCommand", (unsigned long)ttlCommand}, {"slaveofCommand", (unsigned long)slaveofCommand}, {"debugCommand", (unsigned long)debugCommand}, @@ -4594,6 +4680,9 @@ static struct redisFunctionSym symsTable[] = { {"setupSigSegvAction", (unsigned long)setupSigSegvAction}, {"readQueryFromClient", (unsigned long)readQueryFromClient}, {"rdbRemoveTempFile", (unsigned long)rdbRemoveTempFile}, +{"msetGenericCommand", (unsigned long)msetGenericCommand}, +{"msetCommand", (unsigned long)msetCommand}, +{"msetnxCommand", (unsigned long)msetnxCommand}, {NULL,0} };