提交 21dbc649 编写于 作者: A antirez

merge conflict resolved

......@@ -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
......
......@@ -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;
......
......@@ -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);
}
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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 {
......
此差异已折叠。
......@@ -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 */
......
......@@ -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;
......
......@@ -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
......
......@@ -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]);
......
......@@ -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);
......
......@@ -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)}
......
......@@ -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]} {
......
......@@ -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
}
}
......
......@@ -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
......
......@@ -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]]
......
......@@ -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"
......
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}
}
}
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
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 "
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册