summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile2
-rw-r--r--src/aof.c3
-rw-r--r--src/config.c13
-rw-r--r--src/networking.c271
-rw-r--r--src/redis-benchmark.c26
-rw-r--r--src/redis-cli.c4
-rw-r--r--src/redis.c368
-rw-r--r--src/redis.h33
-rw-r--r--src/t_list.c9
-rw-r--r--src/t_set.c5
-rw-r--r--src/t_string.c5
-rw-r--r--src/t_zset.c5
-rw-r--r--tests/support/redis.tcl52
-rw-r--r--tests/support/server.tcl16
-rw-r--r--tests/support/test.tcl4
-rw-r--r--tests/test_helper.tcl42
-rw-r--r--tests/unit/basic.tcl2
-rw-r--r--tests/unit/other.tcl2
-rw-r--r--tests/unit/protocol.tcl106
-rw-r--r--tests/unit/quit.tcl40
-rw-r--r--tests/unit/sort.tcl18
21 files changed, 546 insertions, 480 deletions
diff --git a/src/Makefile b/src/Makefile
index 6f09200e1..ba6ecf4d2 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -132,7 +132,7 @@ dep:
$(CC) -MM *.c
test:
- (cd ..; tclsh8.5 tests/test_helper.tcl --tags "${TAGS}")
+ (cd ..; tclsh8.5 tests/test_helper.tcl --tags "${TAGS}" --file "${FILE}")
bench:
./redis-benchmark
diff --git a/src/aof.c b/src/aof.c
index f7a0c4532..4dbce394c 100644
--- a/src/aof.c
+++ b/src/aof.c
@@ -266,9 +266,6 @@ int loadAppendOnlyFile(char *filename) {
redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", argv[0]->ptr);
exit(1);
}
- /* Try object encoding */
- if (cmd->flags & REDIS_CMD_BULK)
- argv[argc-1] = tryObjectEncoding(argv[argc-1]);
/* Run the command in the context of a fake client */
fakeClient->argc = argc;
fakeClient->argv = argv;
diff --git a/src/config.c b/src/config.c
index 9655917cd..db58a2360 100644
--- a/src/config.c
+++ b/src/config.c
@@ -246,8 +246,11 @@ loaderr:
*----------------------------------------------------------------------------*/
void configSetCommand(redisClient *c) {
- robj *o = getDecodedObject(c->argv[3]);
+ robj *o;
long long ll;
+ redisAssert(c->argv[2]->encoding == REDIS_ENCODING_RAW);
+ redisAssert(c->argv[3]->encoding == REDIS_ENCODING_RAW);
+ o = c->argv[3];
if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) {
zfree(server.dbfilename);
@@ -312,7 +315,6 @@ void configSetCommand(redisClient *c) {
if (startAppendOnly() == REDIS_ERR) {
addReplyError(c,
"Unable to turn on AOF. Check server logs.");
- decrRefCount(o);
return;
}
}
@@ -354,10 +356,8 @@ void configSetCommand(redisClient *c) {
} else {
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
(char*)c->argv[2]->ptr);
- decrRefCount(o);
return;
}
- decrRefCount(o);
addReply(c,shared.ok);
return;
@@ -365,15 +365,15 @@ badfmt: /* Bad format errors */
addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
(char*)o->ptr,
(char*)c->argv[2]->ptr);
- decrRefCount(o);
}
void configGetCommand(redisClient *c) {
- robj *o = getDecodedObject(c->argv[2]);
+ robj *o = c->argv[2];
void *replylen = addDeferredMultiBulkLength(c);
char *pattern = o->ptr;
char buf[128];
int matches = 0;
+ redisAssert(o->encoding == REDIS_ENCODING_RAW);
if (stringmatch(pattern,"dbfilename",0)) {
addReplyBulkCString(c,"dbfilename");
@@ -462,7 +462,6 @@ void configGetCommand(redisClient *c) {
sdsfree(buf);
matches++;
}
- decrRefCount(o);
setDeferredMultiBulkLength(c,replylen,matches*2);
}
diff --git a/src/networking.c b/src/networking.c
index 632fd0476..6181799aa 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -28,13 +28,11 @@ redisClient *createClient(int fd) {
selectDb(c,0);
c->fd = fd;
c->querybuf = sdsempty();
- c->newline = NULL;
+ c->reqtype = 0;
c->argc = 0;
c->argv = NULL;
+ c->multibulklen = 0;
c->bulklen = -1;
- c->multibulk = 0;
- c->mbargc = 0;
- c->mbargv = NULL;
c->sentlen = 0;
c->flags = 0;
c->lastinteraction = time(NULL);
@@ -57,7 +55,12 @@ redisClient *createClient(int fd) {
return c;
}
+/* Set the event loop to listen for write events on the client's socket.
+ * Typically gets called every time a reply is built. */
int _installWriteEvent(redisClient *c) {
+ /* When CLOSE_AFTER_REPLY is set, no more replies may be added! */
+ redisAssert(!(c->flags & REDIS_CLOSE_AFTER_REPLY));
+
if (c->fd <= 0) return REDIS_ERR;
if (c->bufpos == 0 && listLength(c->reply) == 0 &&
(c->replstate == REDIS_REPL_NONE ||
@@ -374,13 +377,9 @@ void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
static void freeClientArgv(redisClient *c) {
int j;
-
for (j = 0; j < c->argc; j++)
decrRefCount(c->argv[j]);
- for (j = 0; j < c->mbargc; j++)
- decrRefCount(c->mbargv[j]);
c->argc = 0;
- c->mbargc = 0;
}
void freeClient(redisClient *c) {
@@ -461,7 +460,6 @@ void freeClient(redisClient *c) {
}
/* Release memory */
zfree(c->argv);
- zfree(c->mbargv);
freeClientMultiState(c);
zfree(c);
}
@@ -546,6 +544,9 @@ void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
if (listLength(c->reply) == 0) {
c->sentlen = 0;
aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
+
+ /* Close connection after entire reply has been sent. */
+ if (c->flags & REDIS_CLOSE_AFTER_REPLY) freeClient(c);
}
}
@@ -630,9 +631,9 @@ void sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int mask)
/* resetClient prepare the client to process the next command */
void resetClient(redisClient *c) {
freeClientArgv(c);
+ c->reqtype = 0;
+ c->multibulklen = 0;
c->bulklen = -1;
- c->multibulk = 0;
- c->newline = NULL;
}
void closeTimedoutClients(void) {
@@ -663,90 +664,172 @@ void closeTimedoutClients(void) {
}
}
-void processInputBuffer(redisClient *c) {
- int seeknewline = 0;
-
-again:
- /* Before to process the input buffer, make sure the client is not
- * waitig for a blocking operation such as BLPOP. Note that the first
- * iteration the client is never blocked, otherwise the processInputBuffer
- * would not be called at all, but after the execution of the first commands
- * in the input buffer the client may be blocked, and the "goto again"
- * will try to reiterate. The following line will make it return asap. */
- if (c->flags & REDIS_BLOCKED || c->flags & REDIS_IO_WAIT) return;
-
- if (seeknewline && c->bulklen == -1) c->newline = strchr(c->querybuf,'\n');
- seeknewline = 1;
- if (c->bulklen == -1) {
- /* Read the first line of the query */
- size_t querylen;
-
- if (c->newline) {
- char *p = c->newline;
- sds query, *argv;
- int argc, j;
-
- c->newline = NULL;
- query = c->querybuf;
- c->querybuf = sdsempty();
- querylen = 1+(p-(query));
- if (sdslen(query) > querylen) {
- /* leave data after the first line of the query in the buffer */
- c->querybuf = sdscatlen(c->querybuf,query+querylen,sdslen(query)-querylen);
- }
- *p = '\0'; /* remove "\n" */
- if (*(p-1) == '\r') *(p-1) = '\0'; /* and "\r" if any */
- sdsupdatelen(query);
-
- /* Now we can split the query in arguments */
- argv = sdssplitlen(query,sdslen(query)," ",1,&argc);
- sdsfree(query);
-
- if (c->argv) zfree(c->argv);
- c->argv = zmalloc(sizeof(robj*)*argc);
-
- for (j = 0; j < argc; j++) {
- if (sdslen(argv[j])) {
- c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);
- c->argc++;
- } else {
- sdsfree(argv[j]);
+int processInlineBuffer(redisClient *c) {
+ char *newline = strstr(c->querybuf,"\r\n");
+ int argc, j;
+ sds *argv;
+ size_t querylen;
+
+ /* Nothing to do without a \r\n */
+ if (newline == NULL)
+ return REDIS_ERR;
+
+ /* Split the input buffer up to the \r\n */
+ querylen = newline-(c->querybuf);
+ argv = sdssplitlen(c->querybuf,querylen," ",1,&argc);
+
+ /* Leave data after the first line of the query in the buffer */
+ c->querybuf = sdsrange(c->querybuf,querylen+2,-1);
+
+ /* Setup argv array on client structure */
+ if (c->argv) zfree(c->argv);
+ c->argv = zmalloc(sizeof(robj*)*argc);
+
+ /* Create redis objects for all arguments. */
+ for (c->argc = 0, j = 0; j < argc; j++) {
+ if (sdslen(argv[j])) {
+ c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);
+ c->argc++;
+ } else {
+ sdsfree(argv[j]);
+ }
+ }
+ zfree(argv);
+ return REDIS_OK;
+}
+
+/* Helper function. Trims query buffer to make the function that processes
+ * multi bulk requests idempotent. */
+static void setProtocolError(redisClient *c, int pos) {
+ c->flags |= REDIS_CLOSE_AFTER_REPLY;
+ c->querybuf = sdsrange(c->querybuf,pos,-1);
+}
+
+int processMultibulkBuffer(redisClient *c) {
+ char *newline = NULL;
+ char *eptr;
+ int pos = 0, tolerr;
+ long bulklen;
+
+ if (c->multibulklen == 0) {
+ /* The client should have been reset */
+ redisAssert(c->argc == 0);
+
+ /* Multi bulk length cannot be read without a \r\n */
+ newline = strstr(c->querybuf,"\r\n");
+ if (newline == NULL)
+ return REDIS_ERR;
+
+ /* We know for sure there is a whole line since newline != NULL,
+ * so go ahead and find out the multi bulk length. */
+ redisAssert(c->querybuf[0] == '*');
+ c->multibulklen = strtol(c->querybuf+1,&eptr,10);
+ pos = (newline-c->querybuf)+2;
+ if (c->multibulklen <= 0) {
+ c->querybuf = sdsrange(c->querybuf,pos,-1);
+ return REDIS_OK;
+ } else if (c->multibulklen > 1024*1024) {
+ addReplyError(c,"Protocol error: invalid multibulk length");
+ setProtocolError(c,pos);
+ return REDIS_ERR;
+ }
+
+ /* Setup argv array on client structure */
+ if (c->argv) zfree(c->argv);
+ c->argv = zmalloc(sizeof(robj*)*c->multibulklen);
+
+ /* Search new newline */
+ newline = strstr(c->querybuf+pos,"\r\n");
+ }
+
+ redisAssert(c->multibulklen > 0);
+ while(c->multibulklen) {
+ /* Read bulk length if unknown */
+ if (c->bulklen == -1) {
+ newline = strstr(c->querybuf+pos,"\r\n");
+ if (newline != NULL) {
+ if (c->querybuf[pos] != '$') {
+ addReplyErrorFormat(c,
+ "Protocol error: expected '$', got '%c'",
+ c->querybuf[pos]);
+ setProtocolError(c,pos);
+ return REDIS_ERR;
}
+
+ bulklen = strtol(c->querybuf+pos+1,&eptr,10);
+ tolerr = (eptr[0] != '\r');
+ if (tolerr || bulklen == LONG_MIN || bulklen == LONG_MAX ||
+ bulklen < 0 || bulklen > 1024*1024*1024)
+ {
+ addReplyError(c,"Protocol error: invalid bulk length");
+ setProtocolError(c,pos);
+ return REDIS_ERR;
+ }
+ pos += eptr-(c->querybuf+pos)+2;
+ c->bulklen = bulklen;
+ } else {
+ /* No newline in current buffer, so wait for more data */
+ break;
}
- zfree(argv);
- if (c->argc) {
- /* Execute the command. If the client is still valid
- * after processCommand() return and there is something
- * on the query buffer try to process the next command. */
- if (processCommand(c) && sdslen(c->querybuf)) goto again;
+ }
+
+ /* Read bulk argument */
+ if (sdslen(c->querybuf)-pos < (unsigned)(c->bulklen+2)) {
+ /* Not enough data (+2 == trailing \r\n) */
+ break;
+ } else {
+ c->argv[c->argc++] = createStringObject(c->querybuf+pos,c->bulklen);
+ pos += c->bulklen+2;
+ c->bulklen = -1;
+ c->multibulklen--;
+ }
+ }
+
+ /* Trim to pos */
+ c->querybuf = sdsrange(c->querybuf,pos,-1);
+
+ /* We're done when c->multibulk == 0 */
+ if (c->multibulklen == 0) {
+ return REDIS_OK;
+ }
+ return REDIS_ERR;
+}
+
+void processInputBuffer(redisClient *c) {
+ /* Keep processing while there is something in the input buffer */
+ while(sdslen(c->querybuf)) {
+ /* Immediately abort if the client is in the middle of something. */
+ if (c->flags & REDIS_BLOCKED || c->flags & REDIS_IO_WAIT) return;
+
+ /* REDIS_CLOSE_AFTER_REPLY closes the connection once the reply is
+ * written to the client. Make sure to not let the reply grow after
+ * this flag has been set (i.e. don't process more commands). */
+ if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
+
+ /* Determine request type when unknown. */
+ if (!c->reqtype) {
+ if (c->querybuf[0] == '*') {
+ c->reqtype = REDIS_REQ_MULTIBULK;
} else {
- /* Nothing to process, argc == 0. Just process the query
- * buffer if it's not empty or return to the caller */
- if (sdslen(c->querybuf)) goto again;
+ c->reqtype = REDIS_REQ_INLINE;
}
- return;
- } else if (sdslen(c->querybuf) >= REDIS_REQUEST_MAX_SIZE) {
- redisLog(REDIS_VERBOSE, "Client protocol error");
- freeClient(c);
- return;
}
- } else {
- /* Bulk read handling. Note that if we are at this point
- the client already sent a command terminated with a newline,
- we are reading the bulk data that is actually the last
- argument of the command. */
- int qbl = sdslen(c->querybuf);
-
- if (c->bulklen <= qbl) {
- /* Copy everything but the final CRLF as final argument */
- c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2);
- c->argc++;
- c->querybuf = sdsrange(c->querybuf,c->bulklen,-1);
- /* 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;
+
+ if (c->reqtype == REDIS_REQ_INLINE) {
+ if (processInlineBuffer(c) != REDIS_OK) break;
+ } else if (c->reqtype == REDIS_REQ_MULTIBULK) {
+ if (processMultibulkBuffer(c) != REDIS_OK) break;
+ } else {
+ redisPanic("Unknown request type");
+ }
+
+ /* Multibulk processing could see a <= 0 length. */
+ if (c->argc == 0) {
+ resetClient(c);
+ } else {
+ /* Only reset the client when the command was executed. */
+ if (processCommand(c) == REDIS_OK)
+ resetClient(c);
}
}
}
@@ -773,14 +856,8 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
return;
}
if (nread) {
- size_t oldlen = sdslen(c->querybuf);
- c->querybuf = sdscatlen(c->querybuf, buf, nread);
+ c->querybuf = sdscatlen(c->querybuf,buf,nread);
c->lastinteraction = time(NULL);
- /* Scan this new piece of the query for the newline. We do this
- * here in order to make sure we perform this scan just one time
- * per piece of buffer, leading to an O(N) scan instead of O(N*N) */
- if (c->bulklen == -1 && c->newline == NULL)
- c->newline = strchr(c->querybuf+oldlen,'\n');
} else {
return;
}
diff --git a/src/redis-benchmark.c b/src/redis-benchmark.c
index 297ecc6c5..c5ababf2a 100644
--- a/src/redis-benchmark.c
+++ b/src/redis-benchmark.c
@@ -575,10 +575,28 @@ int main(int argc, char **argv) {
aeMain(config.el);
endBenchmark();
+ prepareForBenchmark("MSET (10 keys, multi bulk)");
+ c = createClient();
+ if (!c) exit(1);
+ c->obuf = sdscatprintf(c->obuf,"*%d\r\n$4\r\nMSET\r\n", 11);
+ {
+ int i;
+ char *data = zmalloc(config.datasize+2);
+ memset(data,'x',config.datasize);
+ for (i = 0; i < 10; i++) {
+ c->obuf = sdscatprintf(c->obuf,"$%d\r\n%s\r\n",config.datasize,data);
+ }
+ zfree(data);
+ }
+ prepareClientForReply(c,REPLY_RETCODE);
+ createMissingClients(c);
+ aeMain(config.el);
+ endBenchmark();
+
prepareForBenchmark("SET");
c = createClient();
if (!c) exit(1);
- c->obuf = sdscatprintf(c->obuf,"SET foo_rand000000000000 %d\r\n",config.datasize);
+ c->obuf = sdscat(c->obuf,"SET foo_rand000000000000 ");
{
char *data = zmalloc(config.datasize+2);
memset(data,'x',config.datasize);
@@ -612,7 +630,7 @@ int main(int argc, char **argv) {
prepareForBenchmark("LPUSH");
c = createClient();
if (!c) exit(1);
- c->obuf = sdscat(c->obuf,"LPUSH mylist 3\r\nbar\r\n");
+ c->obuf = sdscat(c->obuf,"LPUSH mylist bar\r\n");
prepareClientForReply(c,REPLY_INT);
createMissingClients(c);
aeMain(config.el);
@@ -630,7 +648,7 @@ int main(int argc, char **argv) {
prepareForBenchmark("SADD");
c = createClient();
if (!c) exit(1);
- c->obuf = sdscat(c->obuf,"SADD myset 24\r\ncounter_rand000000000000\r\n");
+ c->obuf = sdscat(c->obuf,"SADD myset counter_rand000000000000\r\n");
prepareClientForReply(c,REPLY_RETCODE);
createMissingClients(c);
aeMain(config.el);
@@ -648,7 +666,7 @@ int main(int argc, char **argv) {
prepareForBenchmark("LPUSH (again, in order to bench LRANGE)");
c = createClient();
if (!c) exit(1);
- c->obuf = sdscat(c->obuf,"LPUSH mylist 3\r\nbar\r\n");
+ c->obuf = sdscat(c->obuf,"LPUSH mylist bar\r\n");
prepareClientForReply(c,REPLY_RETCODE);
createMissingClients(c);
aeMain(config.el);
diff --git a/src/redis-cli.c b/src/redis-cli.c
index 5071604b8..aa7306b44 100644
--- a/src/redis-cli.c
+++ b/src/redis-cli.c
@@ -45,10 +45,6 @@
#include "zmalloc.h"
#include "linenoise.h"
-#define REDIS_CMD_INLINE 1
-#define REDIS_CMD_BULK 2
-#define REDIS_CMD_MULTIBULK 4
-
#define REDIS_NOTUSED(V) ((void) V)
static struct config {
diff --git a/src/redis.c b/src/redis.c
index 08bba542a..4aa195600 100644
--- a/src/redis.c
+++ b/src/redis.c
@@ -69,120 +69,120 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan;
struct redisServer server; /* server global state */
struct redisCommand *commandTable;
struct redisCommand readonlyCommandTable[] = {
- {"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},
- {"setex",setexCommand,4,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},
- {"strlen",strlenCommand,2,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},
- {"rpushx",rpushxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
- {"lpushx",lpushxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
- {"linsert",linsertCommand,5,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},
- {"zunionstore",zunionstoreCommand,-4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
- {"zinterstore",zinterstoreCommand,-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},
- {"zrevrangebyscore",zrevrangebyscoreCommand,-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},
- {"hsetnx",hsetnxCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
- {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
- {"hmset",hmsetCommand,-4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
- {"hmget",hmgetCommand,-3,REDIS_CMD_BULK,NULL,1,1,1},
- {"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,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|REDIS_CMD_DENYOOM,execBlockClientOnSwappedKeys,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},
- {"persist",persistCommand,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},
- {"config",configCommand,-2,REDIS_CMD_BULK,NULL,0,0,0},
- {"subscribe",subscribeCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
- {"unsubscribe",unsubscribeCommand,-1,REDIS_CMD_INLINE,NULL,0,0,0},
- {"psubscribe",psubscribeCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
- {"punsubscribe",punsubscribeCommand,-1,REDIS_CMD_INLINE,NULL,0,0,0},
- {"publish",publishCommand,3,REDIS_CMD_BULK|REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0},
- {"watch",watchCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
- {"unwatch",unwatchCommand,1,REDIS_CMD_INLINE,NULL,0,0,0}
+ {"get",getCommand,2,0,NULL,1,1,1},
+ {"set",setCommand,3,REDIS_CMD_DENYOOM,NULL,0,0,0},
+ {"setnx",setnxCommand,3,REDIS_CMD_DENYOOM,NULL,0,0,0},
+ {"setex",setexCommand,4,REDIS_CMD_DENYOOM,NULL,0,0,0},
+ {"append",appendCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"substr",substrCommand,4,0,NULL,1,1,1},
+ {"strlen",strlenCommand,2,0,NULL,1,1,1},
+ {"del",delCommand,-2,0,NULL,0,0,0},
+ {"exists",existsCommand,2,0,NULL,1,1,1},
+ {"incr",incrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"decr",decrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"mget",mgetCommand,-2,0,NULL,1,-1,1},
+ {"rpush",rpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"lpush",lpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"rpushx",rpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"lpushx",lpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"linsert",linsertCommand,5,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"rpop",rpopCommand,2,0,NULL,1,1,1},
+ {"lpop",lpopCommand,2,0,NULL,1,1,1},
+ {"brpop",brpopCommand,-3,0,NULL,1,1,1},
+ {"blpop",blpopCommand,-3,0,NULL,1,1,1},
+ {"llen",llenCommand,2,0,NULL,1,1,1},
+ {"lindex",lindexCommand,3,0,NULL,1,1,1},
+ {"lset",lsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"lrange",lrangeCommand,4,0,NULL,1,1,1},
+ {"ltrim",ltrimCommand,4,0,NULL,1,1,1},
+ {"lrem",lremCommand,4,0,NULL,1,1,1},
+ {"rpoplpush",rpoplpushcommand,3,REDIS_CMD_DENYOOM,NULL,1,2,1},
+ {"sadd",saddCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"srem",sremCommand,3,0,NULL,1,1,1},
+ {"smove",smoveCommand,4,0,NULL,1,2,1},
+ {"sismember",sismemberCommand,3,0,NULL,1,1,1},
+ {"scard",scardCommand,2,0,NULL,1,1,1},
+ {"spop",spopCommand,2,0,NULL,1,1,1},
+ {"srandmember",srandmemberCommand,2,0,NULL,1,1,1},
+ {"sinter",sinterCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1},
+ {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1},
+ {"sunion",sunionCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1},
+ {"sunionstore",sunionstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1},
+ {"sdiff",sdiffCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1},
+ {"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1},
+ {"smembers",sinterCommand,2,0,NULL,1,1,1},
+ {"zadd",zaddCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"zincrby",zincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"zrem",zremCommand,3,0,NULL,1,1,1},
+ {"zremrangebyscore",zremrangebyscoreCommand,4,0,NULL,1,1,1},
+ {"zremrangebyrank",zremrangebyrankCommand,4,0,NULL,1,1,1},
+ {"zunionstore",zunionstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
+ {"zinterstore",zinterstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
+ {"zrange",zrangeCommand,-4,0,NULL,1,1,1},
+ {"zrangebyscore",zrangebyscoreCommand,-4,0,NULL,1,1,1},
+ {"zrevrangebyscore",zrevrangebyscoreCommand,-4,0,NULL,1,1,1},
+ {"zcount",zcountCommand,4,0,NULL,1,1,1},
+ {"zrevrange",zrevrangeCommand,-4,0,NULL,1,1,1},
+ {"zcard",zcardCommand,2,0,NULL,1,1,1},
+ {"zscore",zscoreCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"zrank",zrankCommand,3,0,NULL,1,1,1},
+ {"zrevrank",zrevrankCommand,3,0,NULL,1,1,1},
+ {"hset",hsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"hsetnx",hsetnxCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"hget",hgetCommand,3,0,NULL,1,1,1},
+ {"hmset",hmsetCommand,-4,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"hmget",hmgetCommand,-3,0,NULL,1,1,1},
+ {"hincrby",hincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"hdel",hdelCommand,3,0,NULL,1,1,1},
+ {"hlen",hlenCommand,2,0,NULL,1,1,1},
+ {"hkeys",hkeysCommand,2,0,NULL,1,1,1},
+ {"hvals",hvalsCommand,2,0,NULL,1,1,1},
+ {"hgetall",hgetallCommand,2,0,NULL,1,1,1},
+ {"hexists",hexistsCommand,3,0,NULL,1,1,1},
+ {"incrby",incrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"decrby",decrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"getset",getsetCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"mset",msetCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2},
+ {"msetnx",msetnxCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2},
+ {"randomkey",randomkeyCommand,1,0,NULL,0,0,0},
+ {"select",selectCommand,2,0,NULL,0,0,0},
+ {"move",moveCommand,3,0,NULL,1,1,1},
+ {"rename",renameCommand,3,0,NULL,1,1,1},
+ {"renamenx",renamenxCommand,3,0,NULL,1,1,1},
+ {"expire",expireCommand,3,0,NULL,0,0,0},
+ {"expireat",expireatCommand,3,0,NULL,0,0,0},
+ {"keys",keysCommand,2,0,NULL,0,0,0},
+ {"dbsize",dbsizeCommand,1,0,NULL,0,0,0},
+ {"auth",authCommand,2,0,NULL,0,0,0},
+ {"ping",pingCommand,1,0,NULL,0,0,0},
+ {"echo",echoCommand,2,0,NULL,0,0,0},
+ {"save",saveCommand,1,0,NULL,0,0,0},
+ {"bgsave",bgsaveCommand,1,0,NULL,0,0,0},
+ {"bgrewriteaof",bgrewriteaofCommand,1,0,NULL,0,0,0},
+ {"shutdown",shutdownCommand,1,0,NULL,0,0,0},
+ {"lastsave",lastsaveCommand,1,0,NULL,0,0,0},
+ {"type",typeCommand,2,0,NULL,1,1,1},
+ {"multi",multiCommand,1,0,NULL,0,0,0},
+ {"exec",execCommand,1,REDIS_CMD_DENYOOM,execBlockClientOnSwappedKeys,0,0,0},
+ {"discard",discardCommand,1,0,NULL,0,0,0},
+ {"sync",syncCommand,1,0,NULL,0,0,0},
+ {"flushdb",flushdbCommand,1,0,NULL,0,0,0},
+ {"flushall",flushallCommand,1,0,NULL,0,0,0},
+ {"sort",sortCommand,-2,REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"info",infoCommand,1,0,NULL,0,0,0},
+ {"monitor",monitorCommand,1,0,NULL,0,0,0},
+ {"ttl",ttlCommand,2,0,NULL,1,1,1},
+ {"persist",persistCommand,2,0,NULL,1,1,1},
+ {"slaveof",slaveofCommand,3,0,NULL,0,0,0},
+ {"debug",debugCommand,-2,0,NULL,0,0,0},
+ {"config",configCommand,-2,0,NULL,0,0,0},
+ {"subscribe",subscribeCommand,-2,0,NULL,0,0,0},
+ {"unsubscribe",unsubscribeCommand,-1,0,NULL,0,0,0},
+ {"psubscribe",psubscribeCommand,-2,0,NULL,0,0,0},
+ {"punsubscribe",punsubscribeCommand,-1,0,NULL,0,0,0},
+ {"publish",publishCommand,3,REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0},
+ {"watch",watchCommand,-2,0,NULL,0,0,0},
+ {"unwatch",unwatchCommand,1,0,NULL,0,0,0}
};
/*============================ Utility functions ============================ */
@@ -899,84 +899,14 @@ void call(redisClient *c, struct redisCommand *cmd) {
int processCommand(redisClient *c) {
struct redisCommand *cmd;
- /* Handle the multi bulk command type. This is an alternative protocol
- * supported by Redis in order to receive commands that are composed of
- * multiple binary-safe "bulk" arguments. The latency of processing is
- * a bit higher but this allows things like multi-sets, so if this
- * protocol is used only for MSET and similar commands this is a big win. */
- if (c->multibulk == 0 && c->argc == 1 && ((char*)(c->argv[0]->ptr))[0] == '*') {
- c->multibulk = atoi(((char*)c->argv[0]->ptr)+1);
- if (c->multibulk <= 0) {
- resetClient(c);
- return 1;
- } else {
- decrRefCount(c->argv[c->argc-1]);
- c->argc--;
- return 1;
- }
- } else if (c->multibulk) {
- if (c->bulklen == -1) {
- if (((char*)c->argv[0]->ptr)[0] != '$') {
- addReplyError(c,"multi bulk protocol error");
- resetClient(c);
- return 1;
- } else {
- char *eptr;
- long bulklen = strtol(((char*)c->argv[0]->ptr)+1,&eptr,10);
- int perr = eptr[0] != '\0';
-
- decrRefCount(c->argv[0]);
- if (perr || bulklen == LONG_MIN || bulklen == LONG_MAX ||
- bulklen < 0 || bulklen > 1024*1024*1024)
- {
- c->argc--;
- addReplyError(c,"invalid bulk write count");
- resetClient(c);
- return 1;
- }
- c->argc--;
- c->bulklen = bulklen+2; /* add two bytes for CR+LF */
- return 1;
- }
- } else {
- c->mbargv = zrealloc(c->mbargv,(sizeof(robj*))*(c->mbargc+1));
- c->mbargv[c->mbargc] = c->argv[0];
- c->mbargc++;
- c->argc--;
- c->multibulk--;
- if (c->multibulk == 0) {
- robj **auxargv;
- int auxargc;
-
- /* Here we need to swap the multi-bulk argc/argv with the
- * normal argc/argv of the client structure. */
- auxargv = c->argv;
- c->argv = c->mbargv;
- c->mbargv = auxargv;
-
- auxargc = c->argc;
- c->argc = c->mbargc;
- c->mbargc = auxargc;
-
- /* We need to set bulklen to something different than -1
- * in order for the code below to process the command without
- * to try to read the last argument of a bulk command as
- * a special argument. */
- c->bulklen = 0;
- /* continue below and process the command */
- } else {
- c->bulklen = -1;
- return 1;
- }
- }
- }
- /* -- end of multi bulk commands processing -- */
-
- /* The QUIT command is handled as a special case. Normal command
- * procs are unable to close the client connection safely */
+ /* The QUIT command is handled separately. Normal command procs will
+ * go through checking for replication and QUIT will cause trouble
+ * when FORCE_REPLICATION is enabled and would be implemented in
+ * a regular command proc. */
if (!strcasecmp(c->argv[0]->ptr,"quit")) {
- freeClient(c);
- return 0;
+ addReply(c,shared.ok);
+ c->flags |= REDIS_CLOSE_AFTER_REPLY;
+ return REDIS_ERR;
}
/* Now lookup the command and check ASAP about trivial error conditions
@@ -985,55 +915,18 @@ int processCommand(redisClient *c) {
if (!cmd) {
addReplyErrorFormat(c,"unknown command '%s'",
(char*)c->argv[0]->ptr);
- resetClient(c);
- return 1;
+ return REDIS_OK;
} else if ((cmd->arity > 0 && cmd->arity != c->argc) ||
(c->argc < -cmd->arity)) {
addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
cmd->name);
- resetClient(c);
- return 1;
- } else if (cmd->flags & REDIS_CMD_BULK && c->bulklen == -1) {
- /* This is a bulk command, we have to read the last argument yet. */
- char *eptr;
- long bulklen = strtol(c->argv[c->argc-1]->ptr,&eptr,10);
- int perr = eptr[0] != '\0';
-
- decrRefCount(c->argv[c->argc-1]);
- if (perr || bulklen == LONG_MAX || bulklen == LONG_MIN ||
- bulklen < 0 || bulklen > 1024*1024*1024)
- {
- c->argc--;
- addReplyError(c,"invalid bulk write count");
- resetClient(c);
- return 1;
- }
- 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.
- * 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++;
- c->querybuf = sdsrange(c->querybuf,c->bulklen,-1);
- } else {
- /* Otherwise return... there is to read the last argument
- * from the socket. */
- return 1;
- }
+ return REDIS_OK;
}
- /* Let's try to encode the bulk object to save space. */
- if (cmd->flags & REDIS_CMD_BULK)
- c->argv[c->argc-1] = tryObjectEncoding(c->argv[c->argc-1]);
/* Check if the user is authenticated */
if (server.requirepass && !c->authenticated && cmd->proc != authCommand) {
addReplyError(c,"operation not permitted");
- resetClient(c);
- return 1;
+ return REDIS_OK;
}
/* Handle the maxmemory directive.
@@ -1046,8 +939,7 @@ int processCommand(redisClient *c) {
zmalloc_used_memory() > server.maxmemory)
{
addReplyError(c,"command not allowed when used memory > 'maxmemory'");
- resetClient(c);
- return 1;
+ return REDIS_OK;
}
/* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
@@ -1056,8 +948,7 @@ int processCommand(redisClient *c) {
cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand &&
cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) {
addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context");
- resetClient(c);
- return 1;
+ return REDIS_OK;
}
/* Exec the command */
@@ -1069,13 +960,10 @@ int processCommand(redisClient *c) {
addReply(c,shared.queued);
} else {
if (server.vm_enabled && server.vm_max_threads > 0 &&
- blockClientOnSwappedKeys(c,cmd)) return 1;
+ blockClientOnSwappedKeys(c,cmd)) return REDIS_ERR;
call(c,cmd);
}
-
- /* Prepare the client for the next command */
- resetClient(c);
- return 1;
+ return REDIS_OK;
}
/*================================== Shutdown =============================== */
diff --git a/src/redis.h b/src/redis.h
index bc961a71a..cf21447d4 100644
--- a/src/redis.h
+++ b/src/redis.h
@@ -57,15 +57,15 @@
/* Hash table parameters */
#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */
-/* Command flags */
-#define REDIS_CMD_BULK 1 /* Bulk write command */
-#define REDIS_CMD_INLINE 2 /* Inline command */
-/* REDIS_CMD_DENYOOM reserves a longer comment: all the commands marked with
- this flags will return an error when the 'maxmemory' option is set in the
- config file and the server is using more than maxmemory bytes of memory.
- In short this commands are denied on low memory conditions. */
-#define REDIS_CMD_DENYOOM 4
-#define REDIS_CMD_FORCE_REPLICATION 8 /* Force replication even if dirty is 0 */
+/* Command flags:
+ * REDIS_CMD_DENYOOM:
+ * Commands marked with this flag will return an error when 'maxmemory' is
+ * set and the server is using more than 'maxmemory' bytes of memory.
+ * In short: commands with this flag are denied on low memory conditions.
+ * REDIS_CMD_FORCE_REPLICATION:
+ * Force replication even if dirty is 0. */
+#define REDIS_CMD_DENYOOM 4
+#define REDIS_CMD_FORCE_REPLICATION 8
/* Object types */
#define REDIS_STRING 0
@@ -144,6 +144,11 @@
#define REDIS_BLOCKED 16 /* The client is waiting in a blocking operation */
#define REDIS_IO_WAIT 32 /* The client is waiting for Virtual Memory I/O */
#define REDIS_DIRTY_CAS 64 /* Watched keys modified. EXEC will fail. */
+#define REDIS_CLOSE_AFTER_REPLY 128 /* Close after writing entire reply. */
+
+/* Client request types */
+#define REDIS_REQ_INLINE 1
+#define REDIS_REQ_MULTIBULK 2
/* Slave replication state - slave side */
#define REDIS_REPL_NONE 0 /* No active replication */
@@ -294,11 +299,11 @@ typedef struct redisClient {
redisDb *db;
int dictid;
sds querybuf;
- robj **argv, **mbargv;
- char *newline; /* pointing to the detected newline in querybuf */
- int argc, mbargc;
- long bulklen; /* bulk read len. -1 if not in bulk read mode */
- int multibulk; /* multi bulk command format active */
+ int argc;
+ robj **argv;
+ int reqtype;
+ int multibulklen; /* number of multi bulk arguments left to read */
+ long bulklen; /* length of bulk argument in multi bulk request */
list *reply;
int sentlen;
time_t lastinteraction; /* time of the last interaction, used for timeout */
diff --git a/src/t_list.c b/src/t_list.c
index 41d651f64..42e1d5873 100644
--- a/src/t_list.c
+++ b/src/t_list.c
@@ -260,6 +260,7 @@ void listTypeConvert(robj *subject, int enc) {
void pushGenericCommand(redisClient *c, int where) {
robj *lobj = lookupKeyWrite(c->db,c->argv[1]);
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
if (lobj == NULL) {
if (handleClientsWaitingListPush(c,c->argv[1],c->argv[2])) {
addReply(c,shared.cone);
@@ -346,14 +347,17 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
}
void lpushxCommand(redisClient *c) {
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
pushxGenericCommand(c,NULL,c->argv[2],REDIS_HEAD);
}
void rpushxCommand(redisClient *c) {
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
pushxGenericCommand(c,NULL,c->argv[2],REDIS_TAIL);
}
void linsertCommand(redisClient *c) {
+ c->argv[4] = tryObjectEncoding(c->argv[4]);
if (strcasecmp(c->argv[2]->ptr,"after") == 0) {
pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_TAIL);
} else if (strcasecmp(c->argv[2]->ptr,"before") == 0) {
@@ -409,7 +413,7 @@ void lsetCommand(redisClient *c) {
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
int index = atoi(c->argv[2]->ptr);
- robj *value = c->argv[3];
+ robj *value = (c->argv[3] = tryObjectEncoding(c->argv[3]));
listTypeTryConversion(o,value);
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
@@ -559,7 +563,8 @@ void ltrimCommand(redisClient *c) {
}
void lremCommand(redisClient *c) {
- robj *subject, *obj = c->argv[3];
+ robj *subject, *obj;
+ obj = c->argv[3] = tryObjectEncoding(c->argv[3]);
int toremove = atoi(c->argv[2]->ptr);
int removed = 0;
listTypeEntry entry;
diff --git a/src/t_set.c b/src/t_set.c
index e2ac5ae53..234efc7de 100644
--- a/src/t_set.c
+++ b/src/t_set.c
@@ -178,6 +178,7 @@ void saddCommand(redisClient *c) {
robj *set;
set = lookupKeyWrite(c->db,c->argv[1]);
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
if (set == NULL) {
set = setTypeCreate(c->argv[2]);
dbAdd(c->db,c->argv[1],set);
@@ -202,6 +203,7 @@ void sremCommand(redisClient *c) {
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,set,REDIS_SET)) return;
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
if (setTypeRemove(set,c->argv[2])) {
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
@@ -216,7 +218,7 @@ void smoveCommand(redisClient *c) {
robj *srcset, *dstset, *ele;
srcset = lookupKeyWrite(c->db,c->argv[1]);
dstset = lookupKeyWrite(c->db,c->argv[2]);
- ele = c->argv[3];
+ ele = c->argv[3] = tryObjectEncoding(c->argv[3]);
/* If the source key does not exist return 0 */
if (srcset == NULL) {
@@ -264,6 +266,7 @@ void sismemberCommand(redisClient *c) {
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,set,REDIS_SET)) return;
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
if (setTypeIsMember(set,c->argv[2]))
addReply(c,shared.cone);
else
diff --git a/src/t_string.c b/src/t_string.c
index 509c630a4..39ee506d5 100644
--- a/src/t_string.c
+++ b/src/t_string.c
@@ -37,14 +37,17 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir
}
void setCommand(redisClient *c) {
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
}
void setnxCommand(redisClient *c) {
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
}
void setexCommand(redisClient *c) {
+ c->argv[3] = tryObjectEncoding(c->argv[3]);
setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
}
@@ -69,6 +72,7 @@ void getCommand(redisClient *c) {
void getsetCommand(redisClient *c) {
if (getGenericCommand(c) == REDIS_ERR) return;
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
dbReplace(c->db,c->argv[1],c->argv[2]);
incrRefCount(c->argv[2]);
touchWatchedKey(c->db,c->argv[1]);
@@ -180,6 +184,7 @@ void appendCommand(redisClient *c) {
robj *o;
o = lookupKeyWrite(c->db,c->argv[1]);
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
if (o == NULL) {
/* Create the key */
retval = dbAdd(c->db,c->argv[1],c->argv[2]);
diff --git a/src/t_zset.c b/src/t_zset.c
index d45e93691..8139b53d8 100644
--- a/src/t_zset.c
+++ b/src/t_zset.c
@@ -440,12 +440,14 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double score, int
void zaddCommand(redisClient *c) {
double scoreval;
if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
+ c->argv[3] = tryObjectEncoding(c->argv[3]);
zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,0);
}
void zincrbyCommand(redisClient *c) {
double scoreval;
if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
+ c->argv[3] = tryObjectEncoding(c->argv[3]);
zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1);
}
@@ -460,6 +462,7 @@ void zremCommand(redisClient *c) {
checkType(c,zsetobj,REDIS_ZSET)) return;
zs = zsetobj->ptr;
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
de = dictFind(zs->dict,c->argv[2]);
if (de == NULL) {
addReply(c,shared.czero);
@@ -1004,6 +1007,7 @@ void zscoreCommand(redisClient *c) {
checkType(c,o,REDIS_ZSET)) return;
zs = o->ptr;
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
de = dictFind(zs->dict,c->argv[2]);
if (!de) {
addReply(c,shared.nullbulk);
@@ -1027,6 +1031,7 @@ void zrankGenericCommand(redisClient *c, int reverse) {
zs = o->ptr;
zsl = zs->zsl;
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
de = dictFind(zs->dict,c->argv[2]);
if (!de) {
addReply(c,shared.nullbulk);
diff --git a/tests/support/redis.tcl b/tests/support/redis.tcl
index 7c7c65c65..4f8ac485d 100644
--- a/tests/support/redis.tcl
+++ b/tests/support/redis.tcl
@@ -36,25 +36,6 @@ array set ::redis::deferred {}
array set ::redis::callback {}
array set ::redis::state {} ;# State in non-blocking reply reading
array set ::redis::statestack {} ;# Stack of states, for nested mbulks
-array set ::redis::bulkarg {}
-array set ::redis::multibulkarg {}
-
-# Flag commands requiring last argument as a bulk write operation
-foreach redis_bulk_cmd {
- set setnx rpush lpush rpushx lpushx linsert lset lrem sadd srem sismember echo getset smove zadd zrem zscore zincrby append zrank zrevrank hget hdel hexists setex publish
-} {
- set ::redis::bulkarg($redis_bulk_cmd) {}
-}
-
-# Flag commands requiring last argument as a bulk write operation
-foreach redis_multibulk_cmd {
- mset msetnx hset hsetnx hmset hmget
-} {
- set ::redis::multibulkarg($redis_multibulk_cmd) {}
-}
-
-unset redis_bulk_cmd
-unset redis_multibulk_cmd
proc redis {{server 127.0.0.1} {port 6379} {defer 0}} {
set fd [socket $server $port]
@@ -79,25 +60,14 @@ proc ::redis::__dispatch__ {id method args} {
set args [lrange $args 0 end-1]
}
if {[info command ::redis::__method__$method] eq {}} {
- if {[info exists ::redis::bulkarg($method)]} {
- set cmd "$method "
- append cmd [join [lrange $args 0 end-1]]
- append cmd " [string length [lindex $args end]]\r\n"
- append cmd [lindex $args end]
- ::redis::redis_writenl $fd $cmd
- } elseif {[info exists ::redis::multibulkarg($method)]} {
- set cmd "*[expr {[llength $args]+1}]\r\n"
- append cmd "$[string length $method]\r\n$method\r\n"
- foreach a $args {
- append cmd "$[string length $a]\r\n$a\r\n"
- }
- ::redis::redis_write $fd $cmd
- flush $fd
- } else {
- set cmd "$method "
- append cmd [join $args]
- ::redis::redis_writenl $fd $cmd
+ set cmd "*[expr {[llength $args]+1}]\r\n"
+ append cmd "$[string length $method]\r\n$method\r\n"
+ foreach a $args {
+ append cmd "$[string length $a]\r\n$a\r\n"
}
+ ::redis::redis_write $fd $cmd
+ flush $fd
+
if {!$deferred} {
if {$blocking} {
::redis::redis_read_reply $fd
@@ -123,6 +93,14 @@ proc ::redis::__method__read {id fd} {
::redis::redis_read_reply $fd
}
+proc ::redis::__method__write {id fd buf} {
+ ::redis::redis_write $fd $buf
+}
+
+proc ::redis::__method__flush {id fd} {
+ flush $fd
+}
+
proc ::redis::__method__close {id fd} {
catch {close $fd}
catch {unset ::redis::fd($id)}
diff --git a/tests/support/server.tcl b/tests/support/server.tcl
index e5ca6c6cd..1507088e0 100644
--- a/tests/support/server.tcl
+++ b/tests/support/server.tcl
@@ -215,7 +215,8 @@ proc start_server {options {code undefined}} {
if {[dict exists $config port]} { set port [dict get $config port] }
# setup config dict
- dict set srv "config" $config_file
+ dict set srv "config_file" $config_file
+ dict set srv "config" $config
dict set srv "pid" $pid
dict set srv "host" $host
dict set srv "port" $port
@@ -238,17 +239,12 @@ proc start_server {options {code undefined}} {
after 10
}
- set client [redis $host $port]
- dict set srv "client" $client
-
- # select the right db when we don't have to authenticate
- if {![dict exists $config requirepass]} {
- $client select 9
- }
-
# append the server to the stack
lappend ::servers $srv
-
+
+ # connect client (after server dict is put on the stack)
+ reconnect
+
# execute provided block
set curnum $::testnum
if {![catch { uplevel 1 $code } err]} {
diff --git a/tests/support/test.tcl b/tests/support/test.tcl
index 93f64928e..e801e1f22 100644
--- a/tests/support/test.tcl
+++ b/tests/support/test.tcl
@@ -90,8 +90,10 @@ proc test {name code {okpattern notspecified}} {
}
}
if {$::traceleaks} {
- if {![string match {*0 leaks*} [exec leaks redis-server]]} {
+ set output [exec leaks redis-server]
+ if {![string match {*0 leaks*} $output]} {
puts "--------- Test $::testnum LEAKED! --------"
+ puts $output
exit 1
}
}
diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl
index ee7fa3e19..4c207f643 100644
--- a/tests/test_helper.tcl
+++ b/tests/test_helper.tcl
@@ -16,6 +16,7 @@ set ::valgrind 0
set ::denytags {}
set ::allowtags {}
set ::external 0; # If "1" this means, we are running against external instance
+set ::file ""; # If set, runs only the tests in this comma separated list
proc execute_tests name {
source "tests/$name.tcl"
@@ -49,6 +50,28 @@ proc r {args} {
[srv $level "client"] {*}$args
}
+proc reconnect {args} {
+ set level [lindex $args 0]
+ if {[string length $level] == 0 || ![string is integer $level]} {
+ set level 0
+ }
+
+ set srv [lindex $::servers end+$level]
+ set host [dict get $srv "host"]
+ set port [dict get $srv "port"]
+ set config [dict get $srv "config"]
+ set client [redis $host $port]
+ dict set srv "client" $client
+
+ # select the right db when we don't have to authenticate
+ if {![dict exists $config "requirepass"]} {
+ $client select 9
+ }
+
+ # re-set $srv in the servers list
+ set ::servers [lreplace $::servers end+$level 1 $srv]
+}
+
proc redis_deferring_client {args} {
set level 0
if {[llength $args] > 0 && [string is integer [lindex $args 0]]} {
@@ -80,8 +103,7 @@ proc cleanup {} {
catch {exec rm -rf {*}[glob tests/tmp/server.*]}
}
-proc main {} {
- cleanup
+proc execute_everything {} {
execute_tests "unit/auth"
execute_tests "unit/protocol"
execute_tests "unit/basic"
@@ -93,6 +115,7 @@ proc main {} {
execute_tests "unit/expire"
execute_tests "unit/other"
execute_tests "unit/cas"
+ execute_tests "unit/quit"
execute_tests "integration/replication"
execute_tests "integration/aof"
# execute_tests "integration/redis-cli"
@@ -110,6 +133,18 @@ proc main {} {
execute_tests "unit/expire"
execute_tests "unit/other"
execute_tests "unit/cas"
+}
+
+proc main {} {
+ cleanup
+
+ if {[string length $::file] > 0} {
+ foreach {file} [split $::file ,] {
+ execute_tests $file
+ }
+ } else {
+ execute_everything
+ }
cleanup
puts "\n[expr $::passed+$::failed] tests, $::passed passed, $::failed failed"
@@ -132,6 +167,9 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
}
}
incr j
+ } elseif {$opt eq {--file}} {
+ set ::file $arg
+ incr j
} elseif {$opt eq {--host}} {
set ::external 1
set ::host $arg
diff --git a/tests/unit/basic.tcl b/tests/unit/basic.tcl
index a8f7feb0b..4c6662c67 100644
--- a/tests/unit/basic.tcl
+++ b/tests/unit/basic.tcl
@@ -172,7 +172,7 @@ start_server {tags {"basic"}} {
test {Commands pipelining} {
set fd [r channel]
- puts -nonewline $fd "SET k1 4\r\nxyzk\r\nGET k1\r\nPING\r\n"
+ puts -nonewline $fd "SET k1 xyzk\r\nGET k1\r\nPING\r\n"
flush $fd
set res {}
append res [string match OK* [::redis::redis_read_reply $fd]]
diff --git a/tests/unit/other.tcl b/tests/unit/other.tcl
index 5967c722d..2e6c0ae17 100644
--- a/tests/unit/other.tcl
+++ b/tests/unit/other.tcl
@@ -123,7 +123,7 @@ start_server {tags {"other"}} {
for {set i 0} {$i < 100000} {incr i} {
set q {}
set val "0000${i}0000"
- append q "SET key:$i [string length $val]\r\n$val\r\n"
+ append q "SET key:$i $val\r\n"
puts -nonewline $fd2 $q
set q {}
append q "GET key:$i\r\n"
diff --git a/tests/unit/protocol.tcl b/tests/unit/protocol.tcl
index 5bf42d7fe..b0faf5dd7 100644
--- a/tests/unit/protocol.tcl
+++ b/tests/unit/protocol.tcl
@@ -1,48 +1,62 @@
start_server {tags {"protocol"}} {
- test {Handle an empty query well} {
- set fd [r channel]
- puts -nonewline $fd "\r\n"
- flush $fd
- r ping
- } {PONG}
-
- test {Negative multi bulk command does not create problems} {
- set fd [r channel]
- puts -nonewline $fd "*-10\r\n"
- flush $fd
- r ping
- } {PONG}
-
- test {Negative multi bulk payload} {
- set fd [r channel]
- puts -nonewline $fd "SET x -10\r\n"
- flush $fd
- gets $fd
- } {*invalid bulk*}
-
- test {Too big bulk payload} {
- set fd [r channel]
- puts -nonewline $fd "SET x 2000000000\r\n"
- flush $fd
- gets $fd
- } {*invalid bulk*count*}
-
- test {bulk payload is not a number} {
- set fd [r channel]
- puts -nonewline $fd "SET x blabla\r\n"
- flush $fd
- gets $fd
- } {*invalid bulk*count*}
-
- test {Multi bulk request not followed by bulk args} {
- set fd [r channel]
- puts -nonewline $fd "*1\r\nfoo\r\n"
- flush $fd
- gets $fd
- } {*protocol error*}
-
- test {Generic wrong number of args} {
- catch {r ping x y z} err
- set _ $err
- } {*wrong*arguments*ping*}
+ test "Handle an empty query" {
+ reconnect
+ r write "\r\n"
+ r flush
+ assert_equal "PONG" [r ping]
+ }
+
+ test "Negative multibulk length" {
+ reconnect
+ r write "*-10\r\n"
+ r flush
+ assert_equal PONG [r ping]
+ }
+
+ test "Out of range multibulk length" {
+ reconnect
+ r write "*20000000\r\n"
+ r flush
+ assert_error "*invalid multibulk length*" {r read}
+ }
+
+ test "Wrong multibulk payload header" {
+ reconnect
+ r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\nfooz\r\n"
+ r flush
+ assert_error "*expected '$', got 'f'*" {r read}
+ }
+
+ test "Negative multibulk payload length" {
+ reconnect
+ r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\n\$-10\r\n"
+ r flush
+ assert_error "*invalid bulk length*" {r read}
+ }
+
+ test "Out of range multibulk payload length" {
+ reconnect
+ r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\n\$2000000000\r\n"
+ r flush
+ assert_error "*invalid bulk length*" {r read}
+ }
+
+ test "Non-number multibulk payload length" {
+ reconnect
+ r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\n\$blabla\r\n"
+ r flush
+ assert_error "*invalid bulk length*" {r read}
+ }
+
+ test "Multi bulk request not followed by bulk arguments" {
+ reconnect
+ r write "*1\r\nfoo\r\n"
+ r flush
+ assert_error "*expected '$', got 'f'*" {r read}
+ }
+
+ test "Generic wrong number of args" {
+ reconnect
+ assert_error "*wrong*arguments*ping*" {r ping x y z}
+ }
}
diff --git a/tests/unit/quit.tcl b/tests/unit/quit.tcl
new file mode 100644
index 000000000..4cf440abf
--- /dev/null
+++ b/tests/unit/quit.tcl
@@ -0,0 +1,40 @@
+start_server {tags {"quit"}} {
+ proc format_command {args} {
+ set cmd "*[llength $args]\r\n"
+ foreach a $args {
+ append cmd "$[string length $a]\r\n$a\r\n"
+ }
+ set _ $cmd
+ }
+
+ test "QUIT returns OK" {
+ reconnect
+ assert_equal OK [r quit]
+ assert_error * {r ping}
+ }
+
+ test "Pipelined commands after QUIT must not be executed" {
+ reconnect
+ r write [format_command quit]
+ r write [format_command set foo bar]
+ r flush
+ assert_equal OK [r read]
+ assert_error * {r read}
+
+ reconnect
+ assert_equal {} [r get foo]
+ }
+
+ test "Pipelined commands after QUIT that exceed read buffer size" {
+ reconnect
+ r write [format_command quit]
+ r write [format_command set foo [string repeat "x" 1024]]
+ r flush
+ assert_equal OK [r read]
+ assert_error * {r read}
+
+ reconnect
+ assert_equal {} [r get foo]
+
+ }
+}
diff --git a/tests/unit/sort.tcl b/tests/unit/sort.tcl
index dcc471fb5..415585223 100644
--- a/tests/unit/sort.tcl
+++ b/tests/unit/sort.tcl
@@ -47,11 +47,11 @@ start_server {
assert_encoding $enc tosort
test "$title: SORT BY key" {
- assert_equal $result [r sort tosort {BY weight_*}]
+ assert_equal $result [r sort tosort BY weight_*]
}
test "$title: SORT BY hash field" {
- assert_equal $result [r sort tosort {BY wobj_*->weight}]
+ assert_equal $result [r sort tosort BY wobj_*->weight]
}
}
@@ -78,21 +78,21 @@ start_server {
}
test "SORT BY key STORE" {
- r sort tosort {BY weight_*} store sort-res
+ r sort tosort BY weight_* store sort-res
assert_equal $result [r lrange sort-res 0 -1]
assert_equal 16 [r llen sort-res]
assert_encoding ziplist sort-res
}
test "SORT BY hash field STORE" {
- r sort tosort {BY wobj_*->weight} store sort-res
+ r sort tosort BY wobj_*->weight store sort-res
assert_equal $result [r lrange sort-res 0 -1]
assert_equal 16 [r llen sort-res]
assert_encoding ziplist sort-res
}
test "SORT DESC" {
- assert_equal [lsort -decreasing -integer $result] [r sort tosort {DESC}]
+ assert_equal [lsort -decreasing -integer $result] [r sort tosort DESC]
}
test "SORT ALPHA against integer encoded strings" {
@@ -141,7 +141,7 @@ start_server {
test "SORT speed, $num element list BY key, 100 times" {
set start [clock clicks -milliseconds]
for {set i 0} {$i < 100} {incr i} {
- set sorted [r sort tosort {BY weight_* LIMIT 0 10}]
+ set sorted [r sort tosort BY weight_* LIMIT 0 10]
}
set elapsed [expr [clock clicks -milliseconds]-$start]
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
@@ -151,7 +151,7 @@ start_server {
test "SORT speed, $num element list BY hash field, 100 times" {
set start [clock clicks -milliseconds]
for {set i 0} {$i < 100} {incr i} {
- set sorted [r sort tosort {BY wobj_*->weight LIMIT 0 10}]
+ set sorted [r sort tosort BY wobj_*->weight LIMIT 0 10]
}
set elapsed [expr [clock clicks -milliseconds]-$start]
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
@@ -161,7 +161,7 @@ start_server {
test "SORT speed, $num element list directly, 100 times" {
set start [clock clicks -milliseconds]
for {set i 0} {$i < 100} {incr i} {
- set sorted [r sort tosort {LIMIT 0 10}]
+ set sorted [r sort tosort LIMIT 0 10]
}
set elapsed [expr [clock clicks -milliseconds]-$start]
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
@@ -171,7 +171,7 @@ start_server {
test "SORT speed, $num element list BY <const>, 100 times" {
set start [clock clicks -milliseconds]
for {set i 0} {$i < 100} {incr i} {
- set sorted [r sort tosort {BY nokey LIMIT 0 10}]
+ set sorted [r sort tosort BY nokey LIMIT 0 10]
}
set elapsed [expr [clock clicks -milliseconds]-$start]
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "