mirror of
https://github.com/fluencelabs/redis
synced 2025-04-26 02:52:15 +00:00
merge conflict resolved
This commit is contained in:
commit
21dbc6499a
@ -132,7 +132,7 @@ dep:
|
|||||||
$(CC) -MM *.c
|
$(CC) -MM *.c
|
||||||
|
|
||||||
test:
|
test:
|
||||||
(cd ..; tclsh8.5 tests/test_helper.tcl --tags "${TAGS}")
|
(cd ..; tclsh8.5 tests/test_helper.tcl --tags "${TAGS}" --file "${FILE}")
|
||||||
|
|
||||||
bench:
|
bench:
|
||||||
./redis-benchmark
|
./redis-benchmark
|
||||||
|
@ -266,9 +266,6 @@ int loadAppendOnlyFile(char *filename) {
|
|||||||
redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", argv[0]->ptr);
|
redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", argv[0]->ptr);
|
||||||
exit(1);
|
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 */
|
/* Run the command in the context of a fake client */
|
||||||
fakeClient->argc = argc;
|
fakeClient->argc = argc;
|
||||||
fakeClient->argv = argv;
|
fakeClient->argv = argv;
|
||||||
|
13
src/config.c
13
src/config.c
@ -246,8 +246,11 @@ loaderr:
|
|||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
void configSetCommand(redisClient *c) {
|
void configSetCommand(redisClient *c) {
|
||||||
robj *o = getDecodedObject(c->argv[3]);
|
robj *o;
|
||||||
long long ll;
|
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")) {
|
if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) {
|
||||||
zfree(server.dbfilename);
|
zfree(server.dbfilename);
|
||||||
@ -312,7 +315,6 @@ void configSetCommand(redisClient *c) {
|
|||||||
if (startAppendOnly() == REDIS_ERR) {
|
if (startAppendOnly() == REDIS_ERR) {
|
||||||
addReplyError(c,
|
addReplyError(c,
|
||||||
"Unable to turn on AOF. Check server logs.");
|
"Unable to turn on AOF. Check server logs.");
|
||||||
decrRefCount(o);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -354,10 +356,8 @@ void configSetCommand(redisClient *c) {
|
|||||||
} else {
|
} else {
|
||||||
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
||||||
(char*)c->argv[2]->ptr);
|
(char*)c->argv[2]->ptr);
|
||||||
decrRefCount(o);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
decrRefCount(o);
|
|
||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -365,15 +365,15 @@ badfmt: /* Bad format errors */
|
|||||||
addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
|
addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
|
||||||
(char*)o->ptr,
|
(char*)o->ptr,
|
||||||
(char*)c->argv[2]->ptr);
|
(char*)c->argv[2]->ptr);
|
||||||
decrRefCount(o);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void configGetCommand(redisClient *c) {
|
void configGetCommand(redisClient *c) {
|
||||||
robj *o = getDecodedObject(c->argv[2]);
|
robj *o = c->argv[2];
|
||||||
void *replylen = addDeferredMultiBulkLength(c);
|
void *replylen = addDeferredMultiBulkLength(c);
|
||||||
char *pattern = o->ptr;
|
char *pattern = o->ptr;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
int matches = 0;
|
int matches = 0;
|
||||||
|
redisAssert(o->encoding == REDIS_ENCODING_RAW);
|
||||||
|
|
||||||
if (stringmatch(pattern,"dbfilename",0)) {
|
if (stringmatch(pattern,"dbfilename",0)) {
|
||||||
addReplyBulkCString(c,"dbfilename");
|
addReplyBulkCString(c,"dbfilename");
|
||||||
@ -462,7 +462,6 @@ void configGetCommand(redisClient *c) {
|
|||||||
sdsfree(buf);
|
sdsfree(buf);
|
||||||
matches++;
|
matches++;
|
||||||
}
|
}
|
||||||
decrRefCount(o);
|
|
||||||
setDeferredMultiBulkLength(c,replylen,matches*2);
|
setDeferredMultiBulkLength(c,replylen,matches*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
269
src/networking.c
269
src/networking.c
@ -28,13 +28,11 @@ redisClient *createClient(int fd) {
|
|||||||
selectDb(c,0);
|
selectDb(c,0);
|
||||||
c->fd = fd;
|
c->fd = fd;
|
||||||
c->querybuf = sdsempty();
|
c->querybuf = sdsempty();
|
||||||
c->newline = NULL;
|
c->reqtype = 0;
|
||||||
c->argc = 0;
|
c->argc = 0;
|
||||||
c->argv = NULL;
|
c->argv = NULL;
|
||||||
|
c->multibulklen = 0;
|
||||||
c->bulklen = -1;
|
c->bulklen = -1;
|
||||||
c->multibulk = 0;
|
|
||||||
c->mbargc = 0;
|
|
||||||
c->mbargv = NULL;
|
|
||||||
c->sentlen = 0;
|
c->sentlen = 0;
|
||||||
c->flags = 0;
|
c->flags = 0;
|
||||||
c->lastinteraction = time(NULL);
|
c->lastinteraction = time(NULL);
|
||||||
@ -57,7 +55,12 @@ redisClient *createClient(int fd) {
|
|||||||
return c;
|
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) {
|
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->fd <= 0) return REDIS_ERR;
|
||||||
if (c->bufpos == 0 && listLength(c->reply) == 0 &&
|
if (c->bufpos == 0 && listLength(c->reply) == 0 &&
|
||||||
(c->replstate == REDIS_REPL_NONE ||
|
(c->replstate == REDIS_REPL_NONE ||
|
||||||
@ -374,13 +377,9 @@ void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
|
|
||||||
static void freeClientArgv(redisClient *c) {
|
static void freeClientArgv(redisClient *c) {
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
for (j = 0; j < c->argc; j++)
|
for (j = 0; j < c->argc; j++)
|
||||||
decrRefCount(c->argv[j]);
|
decrRefCount(c->argv[j]);
|
||||||
for (j = 0; j < c->mbargc; j++)
|
|
||||||
decrRefCount(c->mbargv[j]);
|
|
||||||
c->argc = 0;
|
c->argc = 0;
|
||||||
c->mbargc = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeClient(redisClient *c) {
|
void freeClient(redisClient *c) {
|
||||||
@ -461,7 +460,6 @@ void freeClient(redisClient *c) {
|
|||||||
}
|
}
|
||||||
/* Release memory */
|
/* Release memory */
|
||||||
zfree(c->argv);
|
zfree(c->argv);
|
||||||
zfree(c->mbargv);
|
|
||||||
freeClientMultiState(c);
|
freeClientMultiState(c);
|
||||||
zfree(c);
|
zfree(c);
|
||||||
}
|
}
|
||||||
@ -546,6 +544,9 @@ void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
if (listLength(c->reply) == 0) {
|
if (listLength(c->reply) == 0) {
|
||||||
c->sentlen = 0;
|
c->sentlen = 0;
|
||||||
aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
|
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 */
|
/* resetClient prepare the client to process the next command */
|
||||||
void resetClient(redisClient *c) {
|
void resetClient(redisClient *c) {
|
||||||
freeClientArgv(c);
|
freeClientArgv(c);
|
||||||
|
c->reqtype = 0;
|
||||||
|
c->multibulklen = 0;
|
||||||
c->bulklen = -1;
|
c->bulklen = -1;
|
||||||
c->multibulk = 0;
|
|
||||||
c->newline = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeTimedoutClients(void) {
|
void closeTimedoutClients(void) {
|
||||||
@ -663,90 +664,172 @@ void closeTimedoutClients(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void processInputBuffer(redisClient *c) {
|
int processInlineBuffer(redisClient *c) {
|
||||||
int seeknewline = 0;
|
char *newline = strstr(c->querybuf,"\r\n");
|
||||||
|
int argc, j;
|
||||||
|
sds *argv;
|
||||||
|
size_t querylen;
|
||||||
|
|
||||||
again:
|
/* Nothing to do without a \r\n */
|
||||||
/* Before to process the input buffer, make sure the client is not
|
if (newline == NULL)
|
||||||
* waitig for a blocking operation such as BLPOP. Note that the first
|
return REDIS_ERR;
|
||||||
* 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');
|
/* Split the input buffer up to the \r\n */
|
||||||
seeknewline = 1;
|
querylen = newline-(c->querybuf);
|
||||||
if (c->bulklen == -1) {
|
argv = sdssplitlen(c->querybuf,querylen," ",1,&argc);
|
||||||
/* Read the first line of the query */
|
|
||||||
size_t querylen;
|
|
||||||
|
|
||||||
if (c->newline) {
|
/* Leave data after the first line of the query in the buffer */
|
||||||
char *p = c->newline;
|
c->querybuf = sdsrange(c->querybuf,querylen+2,-1);
|
||||||
sds query, *argv;
|
|
||||||
int argc, j;
|
|
||||||
|
|
||||||
c->newline = NULL;
|
/* Setup argv array on client structure */
|
||||||
query = c->querybuf;
|
if (c->argv) zfree(c->argv);
|
||||||
c->querybuf = sdsempty();
|
c->argv = zmalloc(sizeof(robj*)*argc);
|
||||||
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 */
|
/* Create redis objects for all arguments. */
|
||||||
argv = sdssplitlen(query,sdslen(query)," ",1,&argc);
|
for (c->argc = 0, j = 0; j < argc; j++) {
|
||||||
sdsfree(query);
|
if (sdslen(argv[j])) {
|
||||||
|
c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
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->argc++;
|
||||||
c->querybuf = sdsrange(c->querybuf,c->bulklen,-1);
|
} else {
|
||||||
/* Process the command. If the client is still valid after
|
sdsfree(argv[j]);
|
||||||
* the processing and there is more data in the buffer
|
}
|
||||||
* try to parse it. */
|
}
|
||||||
if (processCommand(c) && sdslen(c->querybuf)) goto again;
|
zfree(argv);
|
||||||
return;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 {
|
||||||
|
c->reqtype = REDIS_REQ_INLINE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (nread) {
|
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);
|
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 {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -575,10 +575,28 @@ int main(int argc, char **argv) {
|
|||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark();
|
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");
|
prepareForBenchmark("SET");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
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);
|
char *data = zmalloc(config.datasize+2);
|
||||||
memset(data,'x',config.datasize);
|
memset(data,'x',config.datasize);
|
||||||
@ -612,7 +630,7 @@ int main(int argc, char **argv) {
|
|||||||
prepareForBenchmark("LPUSH");
|
prepareForBenchmark("LPUSH");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
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);
|
prepareClientForReply(c,REPLY_INT);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
@ -630,7 +648,7 @@ int main(int argc, char **argv) {
|
|||||||
prepareForBenchmark("SADD");
|
prepareForBenchmark("SADD");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
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);
|
prepareClientForReply(c,REPLY_RETCODE);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
@ -648,7 +666,7 @@ int main(int argc, char **argv) {
|
|||||||
prepareForBenchmark("LPUSH (again, in order to bench LRANGE)");
|
prepareForBenchmark("LPUSH (again, in order to bench LRANGE)");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
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);
|
prepareClientForReply(c,REPLY_RETCODE);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
|
@ -45,10 +45,6 @@
|
|||||||
#include "zmalloc.h"
|
#include "zmalloc.h"
|
||||||
#include "linenoise.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)
|
#define REDIS_NOTUSED(V) ((void) V)
|
||||||
|
|
||||||
static struct config {
|
static struct config {
|
||||||
|
368
src/redis.c
368
src/redis.c
@ -69,120 +69,120 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan;
|
|||||||
struct redisServer server; /* server global state */
|
struct redisServer server; /* server global state */
|
||||||
struct redisCommand *commandTable;
|
struct redisCommand *commandTable;
|
||||||
struct redisCommand readonlyCommandTable[] = {
|
struct redisCommand readonlyCommandTable[] = {
|
||||||
{"get",getCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"get",getCommand,2,0,NULL,1,1,1},
|
||||||
{"set",setCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
|
{"set",setCommand,3,REDIS_CMD_DENYOOM,NULL,0,0,0},
|
||||||
{"setnx",setnxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
|
{"setnx",setnxCommand,3,REDIS_CMD_DENYOOM,NULL,0,0,0},
|
||||||
{"setex",setexCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
|
{"setex",setexCommand,4,REDIS_CMD_DENYOOM,NULL,0,0,0},
|
||||||
{"append",appendCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"append",appendCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"substr",substrCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"substr",substrCommand,4,0,NULL,1,1,1},
|
||||||
{"strlen",strlenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"strlen",strlenCommand,2,0,NULL,1,1,1},
|
||||||
{"del",delCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"del",delCommand,-2,0,NULL,0,0,0},
|
||||||
{"exists",existsCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"exists",existsCommand,2,0,NULL,1,1,1},
|
||||||
{"incr",incrCommand,2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"incr",incrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"decr",decrCommand,2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"decr",decrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"mget",mgetCommand,-2,REDIS_CMD_INLINE,NULL,1,-1,1},
|
{"mget",mgetCommand,-2,0,NULL,1,-1,1},
|
||||||
{"rpush",rpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"rpush",rpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"lpush",lpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"lpush",lpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"rpushx",rpushxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"rpushx",rpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"lpushx",lpushxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"lpushx",lpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"linsert",linsertCommand,5,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"linsert",linsertCommand,5,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"rpop",rpopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"rpop",rpopCommand,2,0,NULL,1,1,1},
|
||||||
{"lpop",lpopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"lpop",lpopCommand,2,0,NULL,1,1,1},
|
||||||
{"brpop",brpopCommand,-3,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"brpop",brpopCommand,-3,0,NULL,1,1,1},
|
||||||
{"blpop",blpopCommand,-3,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"blpop",blpopCommand,-3,0,NULL,1,1,1},
|
||||||
{"llen",llenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"llen",llenCommand,2,0,NULL,1,1,1},
|
||||||
{"lindex",lindexCommand,3,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"lindex",lindexCommand,3,0,NULL,1,1,1},
|
||||||
{"lset",lsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"lset",lsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"lrange",lrangeCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"lrange",lrangeCommand,4,0,NULL,1,1,1},
|
||||||
{"ltrim",ltrimCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"ltrim",ltrimCommand,4,0,NULL,1,1,1},
|
||||||
{"lrem",lremCommand,4,REDIS_CMD_BULK,NULL,1,1,1},
|
{"lrem",lremCommand,4,0,NULL,1,1,1},
|
||||||
{"rpoplpush",rpoplpushcommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,2,1},
|
{"rpoplpush",rpoplpushcommand,3,REDIS_CMD_DENYOOM,NULL,1,2,1},
|
||||||
{"sadd",saddCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"sadd",saddCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"srem",sremCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
|
{"srem",sremCommand,3,0,NULL,1,1,1},
|
||||||
{"smove",smoveCommand,4,REDIS_CMD_BULK,NULL,1,2,1},
|
{"smove",smoveCommand,4,0,NULL,1,2,1},
|
||||||
{"sismember",sismemberCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
|
{"sismember",sismemberCommand,3,0,NULL,1,1,1},
|
||||||
{"scard",scardCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"scard",scardCommand,2,0,NULL,1,1,1},
|
||||||
{"spop",spopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"spop",spopCommand,2,0,NULL,1,1,1},
|
||||||
{"srandmember",srandmemberCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"srandmember",srandmemberCommand,2,0,NULL,1,1,1},
|
||||||
{"sinter",sinterCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,-1,1},
|
{"sinter",sinterCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1},
|
||||||
{"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,2,-1,1},
|
{"sinterstore",sinterstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1},
|
||||||
{"sunion",sunionCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,-1,1},
|
{"sunion",sunionCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1},
|
||||||
{"sunionstore",sunionstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,2,-1,1},
|
{"sunionstore",sunionstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1},
|
||||||
{"sdiff",sdiffCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,-1,1},
|
{"sdiff",sdiffCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1},
|
||||||
{"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,2,-1,1},
|
{"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1},
|
||||||
{"smembers",sinterCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"smembers",sinterCommand,2,0,NULL,1,1,1},
|
||||||
{"zadd",zaddCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"zadd",zaddCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"zincrby",zincrbyCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"zincrby",zincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"zrem",zremCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
|
{"zrem",zremCommand,3,0,NULL,1,1,1},
|
||||||
{"zremrangebyscore",zremrangebyscoreCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"zremrangebyscore",zremrangebyscoreCommand,4,0,NULL,1,1,1},
|
||||||
{"zremrangebyrank",zremrangebyrankCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"zremrangebyrank",zremrangebyrankCommand,4,0,NULL,1,1,1},
|
||||||
{"zunionstore",zunionstoreCommand,-4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
|
{"zunionstore",zunionstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
|
||||||
{"zinterstore",zinterstoreCommand,-4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
|
{"zinterstore",zinterstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
|
||||||
{"zrange",zrangeCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"zrange",zrangeCommand,-4,0,NULL,1,1,1},
|
||||||
{"zrangebyscore",zrangebyscoreCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"zrangebyscore",zrangebyscoreCommand,-4,0,NULL,1,1,1},
|
||||||
{"zrevrangebyscore",zrevrangebyscoreCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"zrevrangebyscore",zrevrangebyscoreCommand,-4,0,NULL,1,1,1},
|
||||||
{"zcount",zcountCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"zcount",zcountCommand,4,0,NULL,1,1,1},
|
||||||
{"zrevrange",zrevrangeCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"zrevrange",zrevrangeCommand,-4,0,NULL,1,1,1},
|
||||||
{"zcard",zcardCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"zcard",zcardCommand,2,0,NULL,1,1,1},
|
||||||
{"zscore",zscoreCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"zscore",zscoreCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"zrank",zrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
|
{"zrank",zrankCommand,3,0,NULL,1,1,1},
|
||||||
{"zrevrank",zrevrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
|
{"zrevrank",zrevrankCommand,3,0,NULL,1,1,1},
|
||||||
{"hset",hsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"hset",hsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"hsetnx",hsetnxCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"hsetnx",hsetnxCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
|
{"hget",hgetCommand,3,0,NULL,1,1,1},
|
||||||
{"hmset",hmsetCommand,-4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"hmset",hmsetCommand,-4,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"hmget",hmgetCommand,-3,REDIS_CMD_BULK,NULL,1,1,1},
|
{"hmget",hmgetCommand,-3,0,NULL,1,1,1},
|
||||||
{"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"hincrby",hincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"hdel",hdelCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
|
{"hdel",hdelCommand,3,0,NULL,1,1,1},
|
||||||
{"hlen",hlenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"hlen",hlenCommand,2,0,NULL,1,1,1},
|
||||||
{"hkeys",hkeysCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"hkeys",hkeysCommand,2,0,NULL,1,1,1},
|
||||||
{"hvals",hvalsCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"hvals",hvalsCommand,2,0,NULL,1,1,1},
|
||||||
{"hgetall",hgetallCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"hgetall",hgetallCommand,2,0,NULL,1,1,1},
|
||||||
{"hexists",hexistsCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
|
{"hexists",hexistsCommand,3,0,NULL,1,1,1},
|
||||||
{"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"incrby",incrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"decrby",decrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"decrby",decrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"getset",getsetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"getset",getsetCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"mset",msetCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,-1,2},
|
{"mset",msetCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2},
|
||||||
{"msetnx",msetnxCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,-1,2},
|
{"msetnx",msetnxCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2},
|
||||||
{"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"randomkey",randomkeyCommand,1,0,NULL,0,0,0},
|
||||||
{"select",selectCommand,2,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"select",selectCommand,2,0,NULL,0,0,0},
|
||||||
{"move",moveCommand,3,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"move",moveCommand,3,0,NULL,1,1,1},
|
||||||
{"rename",renameCommand,3,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"rename",renameCommand,3,0,NULL,1,1,1},
|
||||||
{"renamenx",renamenxCommand,3,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"renamenx",renamenxCommand,3,0,NULL,1,1,1},
|
||||||
{"expire",expireCommand,3,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"expire",expireCommand,3,0,NULL,0,0,0},
|
||||||
{"expireat",expireatCommand,3,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"expireat",expireatCommand,3,0,NULL,0,0,0},
|
||||||
{"keys",keysCommand,2,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"keys",keysCommand,2,0,NULL,0,0,0},
|
||||||
{"dbsize",dbsizeCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"dbsize",dbsizeCommand,1,0,NULL,0,0,0},
|
||||||
{"auth",authCommand,2,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"auth",authCommand,2,0,NULL,0,0,0},
|
||||||
{"ping",pingCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"ping",pingCommand,1,0,NULL,0,0,0},
|
||||||
{"echo",echoCommand,2,REDIS_CMD_BULK,NULL,0,0,0},
|
{"echo",echoCommand,2,0,NULL,0,0,0},
|
||||||
{"save",saveCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"save",saveCommand,1,0,NULL,0,0,0},
|
||||||
{"bgsave",bgsaveCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"bgsave",bgsaveCommand,1,0,NULL,0,0,0},
|
||||||
{"bgrewriteaof",bgrewriteaofCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"bgrewriteaof",bgrewriteaofCommand,1,0,NULL,0,0,0},
|
||||||
{"shutdown",shutdownCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"shutdown",shutdownCommand,1,0,NULL,0,0,0},
|
||||||
{"lastsave",lastsaveCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"lastsave",lastsaveCommand,1,0,NULL,0,0,0},
|
||||||
{"type",typeCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"type",typeCommand,2,0,NULL,1,1,1},
|
||||||
{"multi",multiCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"multi",multiCommand,1,0,NULL,0,0,0},
|
||||||
{"exec",execCommand,1,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,execBlockClientOnSwappedKeys,0,0,0},
|
{"exec",execCommand,1,REDIS_CMD_DENYOOM,execBlockClientOnSwappedKeys,0,0,0},
|
||||||
{"discard",discardCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"discard",discardCommand,1,0,NULL,0,0,0},
|
||||||
{"sync",syncCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"sync",syncCommand,1,0,NULL,0,0,0},
|
||||||
{"flushdb",flushdbCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"flushdb",flushdbCommand,1,0,NULL,0,0,0},
|
||||||
{"flushall",flushallCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"flushall",flushallCommand,1,0,NULL,0,0,0},
|
||||||
{"sort",sortCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
|
{"sort",sortCommand,-2,REDIS_CMD_DENYOOM,NULL,1,1,1},
|
||||||
{"info",infoCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"info",infoCommand,1,0,NULL,0,0,0},
|
||||||
{"monitor",monitorCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"monitor",monitorCommand,1,0,NULL,0,0,0},
|
||||||
{"ttl",ttlCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"ttl",ttlCommand,2,0,NULL,1,1,1},
|
||||||
{"persist",persistCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
|
{"persist",persistCommand,2,0,NULL,1,1,1},
|
||||||
{"slaveof",slaveofCommand,3,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"slaveof",slaveofCommand,3,0,NULL,0,0,0},
|
||||||
{"debug",debugCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"debug",debugCommand,-2,0,NULL,0,0,0},
|
||||||
{"config",configCommand,-2,REDIS_CMD_BULK,NULL,0,0,0},
|
{"config",configCommand,-2,0,NULL,0,0,0},
|
||||||
{"subscribe",subscribeCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"subscribe",subscribeCommand,-2,0,NULL,0,0,0},
|
||||||
{"unsubscribe",unsubscribeCommand,-1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"unsubscribe",unsubscribeCommand,-1,0,NULL,0,0,0},
|
||||||
{"psubscribe",psubscribeCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"psubscribe",psubscribeCommand,-2,0,NULL,0,0,0},
|
||||||
{"punsubscribe",punsubscribeCommand,-1,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"punsubscribe",punsubscribeCommand,-1,0,NULL,0,0,0},
|
||||||
{"publish",publishCommand,3,REDIS_CMD_BULK|REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0},
|
{"publish",publishCommand,3,REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0},
|
||||||
{"watch",watchCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
|
{"watch",watchCommand,-2,0,NULL,0,0,0},
|
||||||
{"unwatch",unwatchCommand,1,REDIS_CMD_INLINE,NULL,0,0,0}
|
{"unwatch",unwatchCommand,1,0,NULL,0,0,0}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*============================ Utility functions ============================ */
|
/*============================ Utility functions ============================ */
|
||||||
@ -899,84 +899,14 @@ void call(redisClient *c, struct redisCommand *cmd) {
|
|||||||
int processCommand(redisClient *c) {
|
int processCommand(redisClient *c) {
|
||||||
struct redisCommand *cmd;
|
struct redisCommand *cmd;
|
||||||
|
|
||||||
/* Handle the multi bulk command type. This is an alternative protocol
|
/* The QUIT command is handled separately. Normal command procs will
|
||||||
* supported by Redis in order to receive commands that are composed of
|
* go through checking for replication and QUIT will cause trouble
|
||||||
* multiple binary-safe "bulk" arguments. The latency of processing is
|
* when FORCE_REPLICATION is enabled and would be implemented in
|
||||||
* a bit higher but this allows things like multi-sets, so if this
|
* a regular command proc. */
|
||||||
* 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 */
|
|
||||||
if (!strcasecmp(c->argv[0]->ptr,"quit")) {
|
if (!strcasecmp(c->argv[0]->ptr,"quit")) {
|
||||||
freeClient(c);
|
addReply(c,shared.ok);
|
||||||
return 0;
|
c->flags |= REDIS_CLOSE_AFTER_REPLY;
|
||||||
|
return REDIS_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now lookup the command and check ASAP about trivial error conditions
|
/* Now lookup the command and check ASAP about trivial error conditions
|
||||||
@ -985,55 +915,18 @@ int processCommand(redisClient *c) {
|
|||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
addReplyErrorFormat(c,"unknown command '%s'",
|
addReplyErrorFormat(c,"unknown command '%s'",
|
||||||
(char*)c->argv[0]->ptr);
|
(char*)c->argv[0]->ptr);
|
||||||
resetClient(c);
|
return REDIS_OK;
|
||||||
return 1;
|
|
||||||
} else if ((cmd->arity > 0 && cmd->arity != c->argc) ||
|
} else if ((cmd->arity > 0 && cmd->arity != c->argc) ||
|
||||||
(c->argc < -cmd->arity)) {
|
(c->argc < -cmd->arity)) {
|
||||||
addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
|
addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
|
||||||
cmd->name);
|
cmd->name);
|
||||||
resetClient(c);
|
return REDIS_OK;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* 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 */
|
/* Check if the user is authenticated */
|
||||||
if (server.requirepass && !c->authenticated && cmd->proc != authCommand) {
|
if (server.requirepass && !c->authenticated && cmd->proc != authCommand) {
|
||||||
addReplyError(c,"operation not permitted");
|
addReplyError(c,"operation not permitted");
|
||||||
resetClient(c);
|
return REDIS_OK;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle the maxmemory directive.
|
/* Handle the maxmemory directive.
|
||||||
@ -1046,8 +939,7 @@ int processCommand(redisClient *c) {
|
|||||||
zmalloc_used_memory() > server.maxmemory)
|
zmalloc_used_memory() > server.maxmemory)
|
||||||
{
|
{
|
||||||
addReplyError(c,"command not allowed when used memory > 'maxmemory'");
|
addReplyError(c,"command not allowed when used memory > 'maxmemory'");
|
||||||
resetClient(c);
|
return REDIS_OK;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
|
/* 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 != subscribeCommand && cmd->proc != unsubscribeCommand &&
|
||||||
cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) {
|
cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) {
|
||||||
addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context");
|
addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context");
|
||||||
resetClient(c);
|
return REDIS_OK;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exec the command */
|
/* Exec the command */
|
||||||
@ -1069,13 +960,10 @@ int processCommand(redisClient *c) {
|
|||||||
addReply(c,shared.queued);
|
addReply(c,shared.queued);
|
||||||
} else {
|
} else {
|
||||||
if (server.vm_enabled && server.vm_max_threads > 0 &&
|
if (server.vm_enabled && server.vm_max_threads > 0 &&
|
||||||
blockClientOnSwappedKeys(c,cmd)) return 1;
|
blockClientOnSwappedKeys(c,cmd)) return REDIS_ERR;
|
||||||
call(c,cmd);
|
call(c,cmd);
|
||||||
}
|
}
|
||||||
|
return REDIS_OK;
|
||||||
/* Prepare the client for the next command */
|
|
||||||
resetClient(c);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*================================== Shutdown =============================== */
|
/*================================== Shutdown =============================== */
|
||||||
|
33
src/redis.h
33
src/redis.h
@ -57,15 +57,15 @@
|
|||||||
/* Hash table parameters */
|
/* Hash table parameters */
|
||||||
#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */
|
#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */
|
||||||
|
|
||||||
/* Command flags */
|
/* Command flags:
|
||||||
#define REDIS_CMD_BULK 1 /* Bulk write command */
|
* REDIS_CMD_DENYOOM:
|
||||||
#define REDIS_CMD_INLINE 2 /* Inline command */
|
* Commands marked with this flag will return an error when 'maxmemory' is
|
||||||
/* REDIS_CMD_DENYOOM reserves a longer comment: all the commands marked with
|
* set and the server is using more than 'maxmemory' bytes of memory.
|
||||||
this flags will return an error when the 'maxmemory' option is set in the
|
* In short: commands with this flag are denied on low memory conditions.
|
||||||
config file and the server is using more than maxmemory bytes of memory.
|
* REDIS_CMD_FORCE_REPLICATION:
|
||||||
In short this commands are denied on low memory conditions. */
|
* Force replication even if dirty is 0. */
|
||||||
#define REDIS_CMD_DENYOOM 4
|
#define REDIS_CMD_DENYOOM 4
|
||||||
#define REDIS_CMD_FORCE_REPLICATION 8 /* Force replication even if dirty is 0 */
|
#define REDIS_CMD_FORCE_REPLICATION 8
|
||||||
|
|
||||||
/* Object types */
|
/* Object types */
|
||||||
#define REDIS_STRING 0
|
#define REDIS_STRING 0
|
||||||
@ -144,6 +144,11 @@
|
|||||||
#define REDIS_BLOCKED 16 /* The client is waiting in a blocking operation */
|
#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_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_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 */
|
/* Slave replication state - slave side */
|
||||||
#define REDIS_REPL_NONE 0 /* No active replication */
|
#define REDIS_REPL_NONE 0 /* No active replication */
|
||||||
@ -294,11 +299,11 @@ typedef struct redisClient {
|
|||||||
redisDb *db;
|
redisDb *db;
|
||||||
int dictid;
|
int dictid;
|
||||||
sds querybuf;
|
sds querybuf;
|
||||||
robj **argv, **mbargv;
|
int argc;
|
||||||
char *newline; /* pointing to the detected newline in querybuf */
|
robj **argv;
|
||||||
int argc, mbargc;
|
int reqtype;
|
||||||
long bulklen; /* bulk read len. -1 if not in bulk read mode */
|
int multibulklen; /* number of multi bulk arguments left to read */
|
||||||
int multibulk; /* multi bulk command format active */
|
long bulklen; /* length of bulk argument in multi bulk request */
|
||||||
list *reply;
|
list *reply;
|
||||||
int sentlen;
|
int sentlen;
|
||||||
time_t lastinteraction; /* time of the last interaction, used for timeout */
|
time_t lastinteraction; /* time of the last interaction, used for timeout */
|
||||||
|
@ -260,6 +260,7 @@ void listTypeConvert(robj *subject, int enc) {
|
|||||||
|
|
||||||
void pushGenericCommand(redisClient *c, int where) {
|
void pushGenericCommand(redisClient *c, int where) {
|
||||||
robj *lobj = lookupKeyWrite(c->db,c->argv[1]);
|
robj *lobj = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
if (lobj == NULL) {
|
if (lobj == NULL) {
|
||||||
if (handleClientsWaitingListPush(c,c->argv[1],c->argv[2])) {
|
if (handleClientsWaitingListPush(c,c->argv[1],c->argv[2])) {
|
||||||
addReply(c,shared.cone);
|
addReply(c,shared.cone);
|
||||||
@ -346,14 +347,17 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lpushxCommand(redisClient *c) {
|
void lpushxCommand(redisClient *c) {
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
pushxGenericCommand(c,NULL,c->argv[2],REDIS_HEAD);
|
pushxGenericCommand(c,NULL,c->argv[2],REDIS_HEAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpushxCommand(redisClient *c) {
|
void rpushxCommand(redisClient *c) {
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
pushxGenericCommand(c,NULL,c->argv[2],REDIS_TAIL);
|
pushxGenericCommand(c,NULL,c->argv[2],REDIS_TAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void linsertCommand(redisClient *c) {
|
void linsertCommand(redisClient *c) {
|
||||||
|
c->argv[4] = tryObjectEncoding(c->argv[4]);
|
||||||
if (strcasecmp(c->argv[2]->ptr,"after") == 0) {
|
if (strcasecmp(c->argv[2]->ptr,"after") == 0) {
|
||||||
pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_TAIL);
|
pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_TAIL);
|
||||||
} else if (strcasecmp(c->argv[2]->ptr,"before") == 0) {
|
} 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);
|
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);
|
||||||
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
|
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
|
||||||
int index = atoi(c->argv[2]->ptr);
|
int index = atoi(c->argv[2]->ptr);
|
||||||
robj *value = c->argv[3];
|
robj *value = (c->argv[3] = tryObjectEncoding(c->argv[3]));
|
||||||
|
|
||||||
listTypeTryConversion(o,value);
|
listTypeTryConversion(o,value);
|
||||||
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
|
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||||
@ -559,7 +563,8 @@ void ltrimCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lremCommand(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 toremove = atoi(c->argv[2]->ptr);
|
||||||
int removed = 0;
|
int removed = 0;
|
||||||
listTypeEntry entry;
|
listTypeEntry entry;
|
||||||
|
@ -178,6 +178,7 @@ void saddCommand(redisClient *c) {
|
|||||||
robj *set;
|
robj *set;
|
||||||
|
|
||||||
set = lookupKeyWrite(c->db,c->argv[1]);
|
set = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
if (set == NULL) {
|
if (set == NULL) {
|
||||||
set = setTypeCreate(c->argv[2]);
|
set = setTypeCreate(c->argv[2]);
|
||||||
dbAdd(c->db,c->argv[1],set);
|
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 ||
|
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||||
checkType(c,set,REDIS_SET)) return;
|
checkType(c,set,REDIS_SET)) return;
|
||||||
|
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
if (setTypeRemove(set,c->argv[2])) {
|
if (setTypeRemove(set,c->argv[2])) {
|
||||||
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
|
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
|
||||||
touchWatchedKey(c->db,c->argv[1]);
|
touchWatchedKey(c->db,c->argv[1]);
|
||||||
@ -216,7 +218,7 @@ void smoveCommand(redisClient *c) {
|
|||||||
robj *srcset, *dstset, *ele;
|
robj *srcset, *dstset, *ele;
|
||||||
srcset = lookupKeyWrite(c->db,c->argv[1]);
|
srcset = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
dstset = lookupKeyWrite(c->db,c->argv[2]);
|
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 the source key does not exist return 0 */
|
||||||
if (srcset == NULL) {
|
if (srcset == NULL) {
|
||||||
@ -264,6 +266,7 @@ void sismemberCommand(redisClient *c) {
|
|||||||
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||||
checkType(c,set,REDIS_SET)) return;
|
checkType(c,set,REDIS_SET)) return;
|
||||||
|
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
if (setTypeIsMember(set,c->argv[2]))
|
if (setTypeIsMember(set,c->argv[2]))
|
||||||
addReply(c,shared.cone);
|
addReply(c,shared.cone);
|
||||||
else
|
else
|
||||||
|
@ -37,14 +37,17 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setCommand(redisClient *c) {
|
void setCommand(redisClient *c) {
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
|
setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setnxCommand(redisClient *c) {
|
void setnxCommand(redisClient *c) {
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
|
setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setexCommand(redisClient *c) {
|
void setexCommand(redisClient *c) {
|
||||||
|
c->argv[3] = tryObjectEncoding(c->argv[3]);
|
||||||
setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
|
setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +72,7 @@ void getCommand(redisClient *c) {
|
|||||||
|
|
||||||
void getsetCommand(redisClient *c) {
|
void getsetCommand(redisClient *c) {
|
||||||
if (getGenericCommand(c) == REDIS_ERR) return;
|
if (getGenericCommand(c) == REDIS_ERR) return;
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
dbReplace(c->db,c->argv[1],c->argv[2]);
|
dbReplace(c->db,c->argv[1],c->argv[2]);
|
||||||
incrRefCount(c->argv[2]);
|
incrRefCount(c->argv[2]);
|
||||||
touchWatchedKey(c->db,c->argv[1]);
|
touchWatchedKey(c->db,c->argv[1]);
|
||||||
@ -180,6 +184,7 @@ void appendCommand(redisClient *c) {
|
|||||||
robj *o;
|
robj *o;
|
||||||
|
|
||||||
o = lookupKeyWrite(c->db,c->argv[1]);
|
o = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
if (o == NULL) {
|
if (o == NULL) {
|
||||||
/* Create the key */
|
/* Create the key */
|
||||||
retval = dbAdd(c->db,c->argv[1],c->argv[2]);
|
retval = dbAdd(c->db,c->argv[1],c->argv[2]);
|
||||||
|
@ -440,12 +440,14 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double score, int
|
|||||||
void zaddCommand(redisClient *c) {
|
void zaddCommand(redisClient *c) {
|
||||||
double scoreval;
|
double scoreval;
|
||||||
if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
|
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);
|
zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void zincrbyCommand(redisClient *c) {
|
void zincrbyCommand(redisClient *c) {
|
||||||
double scoreval;
|
double scoreval;
|
||||||
if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
|
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);
|
zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,6 +462,7 @@ void zremCommand(redisClient *c) {
|
|||||||
checkType(c,zsetobj,REDIS_ZSET)) return;
|
checkType(c,zsetobj,REDIS_ZSET)) return;
|
||||||
|
|
||||||
zs = zsetobj->ptr;
|
zs = zsetobj->ptr;
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
de = dictFind(zs->dict,c->argv[2]);
|
de = dictFind(zs->dict,c->argv[2]);
|
||||||
if (de == NULL) {
|
if (de == NULL) {
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
@ -1004,6 +1007,7 @@ void zscoreCommand(redisClient *c) {
|
|||||||
checkType(c,o,REDIS_ZSET)) return;
|
checkType(c,o,REDIS_ZSET)) return;
|
||||||
|
|
||||||
zs = o->ptr;
|
zs = o->ptr;
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
de = dictFind(zs->dict,c->argv[2]);
|
de = dictFind(zs->dict,c->argv[2]);
|
||||||
if (!de) {
|
if (!de) {
|
||||||
addReply(c,shared.nullbulk);
|
addReply(c,shared.nullbulk);
|
||||||
@ -1027,6 +1031,7 @@ void zrankGenericCommand(redisClient *c, int reverse) {
|
|||||||
|
|
||||||
zs = o->ptr;
|
zs = o->ptr;
|
||||||
zsl = zs->zsl;
|
zsl = zs->zsl;
|
||||||
|
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
de = dictFind(zs->dict,c->argv[2]);
|
de = dictFind(zs->dict,c->argv[2]);
|
||||||
if (!de) {
|
if (!de) {
|
||||||
addReply(c,shared.nullbulk);
|
addReply(c,shared.nullbulk);
|
||||||
|
@ -36,25 +36,6 @@ array set ::redis::deferred {}
|
|||||||
array set ::redis::callback {}
|
array set ::redis::callback {}
|
||||||
array set ::redis::state {} ;# State in non-blocking reply reading
|
array set ::redis::state {} ;# State in non-blocking reply reading
|
||||||
array set ::redis::statestack {} ;# Stack of states, for nested mbulks
|
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}} {
|
proc redis {{server 127.0.0.1} {port 6379} {defer 0}} {
|
||||||
set fd [socket $server $port]
|
set fd [socket $server $port]
|
||||||
@ -79,25 +60,14 @@ proc ::redis::__dispatch__ {id method args} {
|
|||||||
set args [lrange $args 0 end-1]
|
set args [lrange $args 0 end-1]
|
||||||
}
|
}
|
||||||
if {[info command ::redis::__method__$method] eq {}} {
|
if {[info command ::redis::__method__$method] eq {}} {
|
||||||
if {[info exists ::redis::bulkarg($method)]} {
|
set cmd "*[expr {[llength $args]+1}]\r\n"
|
||||||
set cmd "$method "
|
append cmd "$[string length $method]\r\n$method\r\n"
|
||||||
append cmd [join [lrange $args 0 end-1]]
|
foreach a $args {
|
||||||
append cmd " [string length [lindex $args end]]\r\n"
|
append cmd "$[string length $a]\r\n$a\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
|
|
||||||
}
|
}
|
||||||
|
::redis::redis_write $fd $cmd
|
||||||
|
flush $fd
|
||||||
|
|
||||||
if {!$deferred} {
|
if {!$deferred} {
|
||||||
if {$blocking} {
|
if {$blocking} {
|
||||||
::redis::redis_read_reply $fd
|
::redis::redis_read_reply $fd
|
||||||
@ -123,6 +93,14 @@ proc ::redis::__method__read {id fd} {
|
|||||||
::redis::redis_read_reply $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} {
|
proc ::redis::__method__close {id fd} {
|
||||||
catch {close $fd}
|
catch {close $fd}
|
||||||
catch {unset ::redis::fd($id)}
|
catch {unset ::redis::fd($id)}
|
||||||
|
@ -215,7 +215,8 @@ proc start_server {options {code undefined}} {
|
|||||||
if {[dict exists $config port]} { set port [dict get $config port] }
|
if {[dict exists $config port]} { set port [dict get $config port] }
|
||||||
|
|
||||||
# setup config dict
|
# 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 "pid" $pid
|
||||||
dict set srv "host" $host
|
dict set srv "host" $host
|
||||||
dict set srv "port" $port
|
dict set srv "port" $port
|
||||||
@ -238,17 +239,12 @@ proc start_server {options {code undefined}} {
|
|||||||
after 10
|
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
|
# append the server to the stack
|
||||||
lappend ::servers $srv
|
lappend ::servers $srv
|
||||||
|
|
||||||
|
# connect client (after server dict is put on the stack)
|
||||||
|
reconnect
|
||||||
|
|
||||||
# execute provided block
|
# execute provided block
|
||||||
set curnum $::testnum
|
set curnum $::testnum
|
||||||
if {![catch { uplevel 1 $code } err]} {
|
if {![catch { uplevel 1 $code } err]} {
|
||||||
|
@ -90,8 +90,10 @@ proc test {name code {okpattern notspecified}} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if {$::traceleaks} {
|
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 "--------- Test $::testnum LEAKED! --------"
|
||||||
|
puts $output
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ set ::valgrind 0
|
|||||||
set ::denytags {}
|
set ::denytags {}
|
||||||
set ::allowtags {}
|
set ::allowtags {}
|
||||||
set ::external 0; # If "1" this means, we are running against external instance
|
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 {
|
proc execute_tests name {
|
||||||
source "tests/$name.tcl"
|
source "tests/$name.tcl"
|
||||||
@ -49,6 +50,28 @@ proc r {args} {
|
|||||||
[srv $level "client"] {*}$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} {
|
proc redis_deferring_client {args} {
|
||||||
set level 0
|
set level 0
|
||||||
if {[llength $args] > 0 && [string is integer [lindex $args 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.*]}
|
catch {exec rm -rf {*}[glob tests/tmp/server.*]}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc main {} {
|
proc execute_everything {} {
|
||||||
cleanup
|
|
||||||
execute_tests "unit/auth"
|
execute_tests "unit/auth"
|
||||||
execute_tests "unit/protocol"
|
execute_tests "unit/protocol"
|
||||||
execute_tests "unit/basic"
|
execute_tests "unit/basic"
|
||||||
@ -93,6 +115,7 @@ proc main {} {
|
|||||||
execute_tests "unit/expire"
|
execute_tests "unit/expire"
|
||||||
execute_tests "unit/other"
|
execute_tests "unit/other"
|
||||||
execute_tests "unit/cas"
|
execute_tests "unit/cas"
|
||||||
|
execute_tests "unit/quit"
|
||||||
execute_tests "integration/replication"
|
execute_tests "integration/replication"
|
||||||
execute_tests "integration/aof"
|
execute_tests "integration/aof"
|
||||||
# execute_tests "integration/redis-cli"
|
# execute_tests "integration/redis-cli"
|
||||||
@ -110,6 +133,18 @@ proc main {} {
|
|||||||
execute_tests "unit/expire"
|
execute_tests "unit/expire"
|
||||||
execute_tests "unit/other"
|
execute_tests "unit/other"
|
||||||
execute_tests "unit/cas"
|
execute_tests "unit/cas"
|
||||||
|
}
|
||||||
|
|
||||||
|
proc main {} {
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
if {[string length $::file] > 0} {
|
||||||
|
foreach {file} [split $::file ,] {
|
||||||
|
execute_tests $file
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
execute_everything
|
||||||
|
}
|
||||||
|
|
||||||
cleanup
|
cleanup
|
||||||
puts "\n[expr $::passed+$::failed] tests, $::passed passed, $::failed failed"
|
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
|
incr j
|
||||||
|
} elseif {$opt eq {--file}} {
|
||||||
|
set ::file $arg
|
||||||
|
incr j
|
||||||
} elseif {$opt eq {--host}} {
|
} elseif {$opt eq {--host}} {
|
||||||
set ::external 1
|
set ::external 1
|
||||||
set ::host $arg
|
set ::host $arg
|
||||||
|
@ -172,7 +172,7 @@ start_server {tags {"basic"}} {
|
|||||||
|
|
||||||
test {Commands pipelining} {
|
test {Commands pipelining} {
|
||||||
set fd [r channel]
|
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
|
flush $fd
|
||||||
set res {}
|
set res {}
|
||||||
append res [string match OK* [::redis::redis_read_reply $fd]]
|
append res [string match OK* [::redis::redis_read_reply $fd]]
|
||||||
|
@ -123,7 +123,7 @@ start_server {tags {"other"}} {
|
|||||||
for {set i 0} {$i < 100000} {incr i} {
|
for {set i 0} {$i < 100000} {incr i} {
|
||||||
set q {}
|
set q {}
|
||||||
set val "0000${i}0000"
|
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
|
puts -nonewline $fd2 $q
|
||||||
set q {}
|
set q {}
|
||||||
append q "GET key:$i\r\n"
|
append q "GET key:$i\r\n"
|
||||||
|
@ -1,48 +1,62 @@
|
|||||||
start_server {tags {"protocol"}} {
|
start_server {tags {"protocol"}} {
|
||||||
test {Handle an empty query well} {
|
test "Handle an empty query" {
|
||||||
set fd [r channel]
|
reconnect
|
||||||
puts -nonewline $fd "\r\n"
|
r write "\r\n"
|
||||||
flush $fd
|
r flush
|
||||||
r ping
|
assert_equal "PONG" [r ping]
|
||||||
} {PONG}
|
}
|
||||||
|
|
||||||
test {Negative multi bulk command does not create problems} {
|
test "Negative multibulk length" {
|
||||||
set fd [r channel]
|
reconnect
|
||||||
puts -nonewline $fd "*-10\r\n"
|
r write "*-10\r\n"
|
||||||
flush $fd
|
r flush
|
||||||
r ping
|
assert_equal PONG [r ping]
|
||||||
} {PONG}
|
}
|
||||||
|
|
||||||
test {Negative multi bulk payload} {
|
test "Out of range multibulk length" {
|
||||||
set fd [r channel]
|
reconnect
|
||||||
puts -nonewline $fd "SET x -10\r\n"
|
r write "*20000000\r\n"
|
||||||
flush $fd
|
r flush
|
||||||
gets $fd
|
assert_error "*invalid multibulk length*" {r read}
|
||||||
} {*invalid bulk*}
|
}
|
||||||
|
|
||||||
test {Too big bulk payload} {
|
test "Wrong multibulk payload header" {
|
||||||
set fd [r channel]
|
reconnect
|
||||||
puts -nonewline $fd "SET x 2000000000\r\n"
|
r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\nfooz\r\n"
|
||||||
flush $fd
|
r flush
|
||||||
gets $fd
|
assert_error "*expected '$', got 'f'*" {r read}
|
||||||
} {*invalid bulk*count*}
|
}
|
||||||
|
|
||||||
test {bulk payload is not a number} {
|
test "Negative multibulk payload length" {
|
||||||
set fd [r channel]
|
reconnect
|
||||||
puts -nonewline $fd "SET x blabla\r\n"
|
r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\n\$-10\r\n"
|
||||||
flush $fd
|
r flush
|
||||||
gets $fd
|
assert_error "*invalid bulk length*" {r read}
|
||||||
} {*invalid bulk*count*}
|
}
|
||||||
|
|
||||||
test {Multi bulk request not followed by bulk args} {
|
test "Out of range multibulk payload length" {
|
||||||
set fd [r channel]
|
reconnect
|
||||||
puts -nonewline $fd "*1\r\nfoo\r\n"
|
r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\n\$2000000000\r\n"
|
||||||
flush $fd
|
r flush
|
||||||
gets $fd
|
assert_error "*invalid bulk length*" {r read}
|
||||||
} {*protocol error*}
|
}
|
||||||
|
|
||||||
test {Generic wrong number of args} {
|
test "Non-number multibulk payload length" {
|
||||||
catch {r ping x y z} err
|
reconnect
|
||||||
set _ $err
|
r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\n\$blabla\r\n"
|
||||||
} {*wrong*arguments*ping*}
|
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}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
40
tests/unit/quit.tcl
Normal file
40
tests/unit/quit.tcl
Normal file
@ -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]
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -47,11 +47,11 @@ start_server {
|
|||||||
assert_encoding $enc tosort
|
assert_encoding $enc tosort
|
||||||
|
|
||||||
test "$title: SORT BY key" {
|
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" {
|
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" {
|
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 $result [r lrange sort-res 0 -1]
|
||||||
assert_equal 16 [r llen sort-res]
|
assert_equal 16 [r llen sort-res]
|
||||||
assert_encoding ziplist sort-res
|
assert_encoding ziplist sort-res
|
||||||
}
|
}
|
||||||
|
|
||||||
test "SORT BY hash field STORE" {
|
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 $result [r lrange sort-res 0 -1]
|
||||||
assert_equal 16 [r llen sort-res]
|
assert_equal 16 [r llen sort-res]
|
||||||
assert_encoding ziplist sort-res
|
assert_encoding ziplist sort-res
|
||||||
}
|
}
|
||||||
|
|
||||||
test "SORT DESC" {
|
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" {
|
test "SORT ALPHA against integer encoded strings" {
|
||||||
@ -141,7 +141,7 @@ start_server {
|
|||||||
test "SORT speed, $num element list BY key, 100 times" {
|
test "SORT speed, $num element list BY key, 100 times" {
|
||||||
set start [clock clicks -milliseconds]
|
set start [clock clicks -milliseconds]
|
||||||
for {set i 0} {$i < 100} {incr i} {
|
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]
|
set elapsed [expr [clock clicks -milliseconds]-$start]
|
||||||
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
|
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" {
|
test "SORT speed, $num element list BY hash field, 100 times" {
|
||||||
set start [clock clicks -milliseconds]
|
set start [clock clicks -milliseconds]
|
||||||
for {set i 0} {$i < 100} {incr i} {
|
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]
|
set elapsed [expr [clock clicks -milliseconds]-$start]
|
||||||
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
|
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" {
|
test "SORT speed, $num element list directly, 100 times" {
|
||||||
set start [clock clicks -milliseconds]
|
set start [clock clicks -milliseconds]
|
||||||
for {set i 0} {$i < 100} {incr i} {
|
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]
|
set elapsed [expr [clock clicks -milliseconds]-$start]
|
||||||
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
|
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" {
|
test "SORT speed, $num element list BY <const>, 100 times" {
|
||||||
set start [clock clicks -milliseconds]
|
set start [clock clicks -milliseconds]
|
||||||
for {set i 0} {$i < 100} {incr i} {
|
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]
|
set elapsed [expr [clock clicks -milliseconds]-$start]
|
||||||
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
|
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
|
||||||
|
Loading…
x
Reference in New Issue
Block a user