* POSSIBILITY OF SUCH DAMAGE.
*/
-#define REDIS_VERSION "1.100"
+#define REDIS_VERSION "1.1.94"
#include "fmacros.h"
#include "config.h"
char *appendfilename;
char *requirepass;
int shareobjects;
+ int rdbcompression;
/* Replication related */
int isslave;
char *masterauth;
{"zincrby",zincrbyCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
{"zrem",zremCommand,3,REDIS_CMD_BULK},
{"zremrangebyscore",zremrangebyscoreCommand,4,REDIS_CMD_INLINE},
- {"zrange",zrangeCommand,4,REDIS_CMD_INLINE},
+ {"zrange",zrangeCommand,-4,REDIS_CMD_INLINE},
{"zrangebyscore",zrangebyscoreCommand,-4,REDIS_CMD_INLINE},
- {"zrevrange",zrevrangeCommand,4,REDIS_CMD_INLINE},
+ {"zrevrange",zrevrangeCommand,-4,REDIS_CMD_INLINE},
{"zcard",zcardCommand,2,REDIS_CMD_INLINE},
{"zscore",zscoreCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
{"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
* is based on heap allocation for send buffers, so we simply abort.
* At least the code will be simpler to read... */
static void oom(const char *msg) {
- fprintf(stderr, "%s: Out of memory\n",msg);
- fflush(stderr);
+ redisLog(REDIS_WARNING, "%s: Out of memory\n",msg);
sleep(1);
abort();
}
close(fd);
goto cleanup;
}
- redisLog(REDIS_WARNING,"Parent diff flushed into the new append log file with success");
+ redisLog(REDIS_NOTICE,"Parent diff flushed into the new append log file with success (%lu bytes)",sdslen(server.bgrewritebuf));
/* Now our work is to rename the temp file into the stable file. And
* switch the file descriptor used by the server for append only. */
if (rename(tmpfile,server.appendfilename) == -1) {
shared.nullbulk = createObject(REDIS_STRING,sdsnew("$-1\r\n"));
shared.nullmultibulk = createObject(REDIS_STRING,sdsnew("*-1\r\n"));
shared.emptymultibulk = createObject(REDIS_STRING,sdsnew("*0\r\n"));
- /* no such key */
shared.pong = createObject(REDIS_STRING,sdsnew("+PONG\r\n"));
shared.wrongtypeerr = createObject(REDIS_STRING,sdsnew(
"-ERR Operation against a key holding the wrong kind of value\r\n"));
server.appendfilename = "appendonly.aof";
server.requirepass = NULL;
server.shareobjects = 0;
+ server.rdbcompression = 1;
server.sharingpoolsize = 1024;
server.maxclients = 0;
server.maxmemory = 0;
if ((server.shareobjects = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
+ } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
+ if ((server.rdbcompression = yesnotoi(argv[1])) == -1) {
+ err = "argument must be 'yes' or 'no'"; goto loaderr;
+ }
} else if (!strcasecmp(argv[0],"shareobjectspoolsize") && argc == 2) {
server.sharingpoolsize = atoi(argv[1]);
if (server.sharingpoolsize < 1) {
}
cmd = lookupCommand(c->argv[0]->ptr);
if (!cmd) {
- addReplySds(c,sdsnew("-ERR unknown command\r\n"));
+ addReplySds(c,
+ sdscatprintf(sdsempty(), "-ERR unknown command '%s'\r\n",
+ (char*)c->argv[0]->ptr));
resetClient(c);
return 1;
} else if ((cmd->arity > 0 && cmd->arity != c->argc) ||
(c->argc < -cmd->arity)) {
- addReplySds(c,sdsnew("-ERR wrong number of arguments\r\n"));
+ addReplySds(c,
+ sdscatprintf(sdsempty(),
+ "-ERR wrong number of arguments for '%s' command\r\n",
+ cmd->name));
resetClient(c);
return 1;
} else if (server.maxmemory && cmd->flags & REDIS_CMD_DENYOOM && zmalloc_used_memory() > server.maxmemory) {
robj *lenobj;
lenobj = createObject(REDIS_STRING,
- sdscatprintf(sdsempty(),"%d\r\n",
- stringObjectLen(argv[j])));
+ sdscatprintf(sdsempty(),"%lu\r\n",
+ (unsigned long) stringObjectLen(argv[j])));
lenobj->refcount = 0;
outv[outc++] = lenobj;
}
sdsupdatelen(query);
/* Now we can split the query in arguments */
- if (sdslen(query) == 0) {
- /* Ignore empty query */
- sdsfree(query);
- return;
- }
argv = sdssplitlen(query,sdslen(query)," ",1,&argc);
sdsfree(query);
}
}
zfree(argv);
- /* 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 (c->argc && processCommand(c) && sdslen(c->querybuf)) goto again;
+ 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_DEBUG, "Client protocol error");
char buf[128];
snprintf(buf,sizeof(buf),"%.17g",d);
- addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n%s\r\n",
- strlen(buf),buf));
+ addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n%s\r\n",
+ (unsigned long) strlen(buf),buf));
}
static void addReplyBulkLen(redisClient *c, robj *obj) {
} else {
long n = (long)obj->ptr;
+ /* Compute how many bytes will take this integer as a radix 10 string */
len = 1;
if (n < 0) {
len++;
len++;
}
}
- addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",len));
+ addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n",(unsigned long)len));
}
static void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Try LZF compression - under 20 bytes it's unable to compress even
* aaaaaaaaaaaaaaaaaa so skip it */
- if (len > 20) {
+ if (server.rdbcompression && len > 20) {
int retval;
retval = rdbSaveLzfStringObject(fp,obj);
case 253: *val = R_Nan; return 0;
default:
if (fread(buf,len,1,fp) == 0) return -1;
+ buf[len] = '\0';
sscanf(buf, "%lg", val);
return 0;
}
static void setGenericCommand(redisClient *c, int nx) {
int retval;
+ if (nx) deleteIfVolatile(c->db,c->argv[1]);
retval = dictAdd(c->db->dict,c->argv[1],c->argv[2]);
if (retval == DICT_ERR) {
if (!nx) {
}
static void msetGenericCommand(redisClient *c, int nx) {
- int j;
+ int j, busykeys = 0;
if ((c->argc % 2) == 0) {
- addReplySds(c,sdsnew("-ERR wrong number of arguments\r\n"));
+ addReplySds(c,sdsnew("-ERR wrong number of arguments for MSET\r\n"));
return;
}
/* Handle the NX flag. The MSETNX semantic is to return zero and don't
* set nothing at all if at least one already key exists. */
if (nx) {
for (j = 1; j < c->argc; j += 2) {
- if (dictFind(c->db->dict,c->argv[j]) != NULL) {
- addReply(c, shared.czero);
- return;
+ if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
+ busykeys++;
}
}
}
+ if (busykeys) {
+ addReply(c, shared.czero);
+ return;
+ }
for (j = 1; j < c->argc; j += 2) {
int retval;
dictEntry *de;
sds pattern = c->argv[1]->ptr;
int plen = sdslen(pattern);
- int numkeys = 0, keyslen = 0;
+ unsigned long numkeys = 0, keyslen = 0;
robj *lenobj = createObject(REDIS_STRING,NULL);
di = dictGetIterator(c->db->dict);
return;
}
if (rdbSaveBackground(server.dbfilename) == REDIS_OK) {
- addReply(c,shared.ok);
+ char *status = "+Background saving started\r\n";
+ addReplySds(c,sdsnew(status));
} else {
addReply(c,shared.err);
}
kill(server.bgsavechildpid,SIGKILL);
rdbRemoveTempFile(server.bgsavechildpid);
}
- /* SYNC SAVE */
- if (rdbSave(server.dbfilename) == REDIS_OK) {
- if (server.daemonize)
- unlink(server.pidfile);
- redisLog(REDIS_WARNING,"%zu bytes used at exit",zmalloc_used_memory());
- redisLog(REDIS_WARNING,"Server exit now, bye bye...");
- exit(1);
+ if (server.appendonly) {
+ /* Append only file: fsync() the AOF and exit */
+ fsync(server.appendfd);
+ exit(0);
} else {
- /* Ooops.. error saving! The best we can do is to continue operating.
- * Note that if there was a background saving process, in the next
- * cron() Redis will be notified that the background saving aborted,
- * handling special stuff like slaves pending for synchronization... */
- redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit");
- addReplySds(c,sdsnew("-ERR can't quit, problems saving the DB\r\n"));
+ /* Snapshotting. Perform a SYNC SAVE and exit */
+ if (rdbSave(server.dbfilename) == REDIS_OK) {
+ if (server.daemonize)
+ unlink(server.pidfile);
+ redisLog(REDIS_WARNING,"%zu bytes used at exit",zmalloc_used_memory());
+ redisLog(REDIS_WARNING,"Server exit now, bye bye...");
+ exit(0);
+ } else {
+ /* Ooops.. error saving! The best we can do is to continue operating.
+ * Note that if there was a background saving process, in the next
+ * cron() Redis will be notified that the background saving aborted,
+ * handling special stuff like slaves pending for synchronization... */
+ redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit");
+ addReplySds(c,sdsnew("-ERR can't quit, problems saving the DB\r\n"));
+ }
}
}
o = lookupKeyWrite(c->db,c->argv[1]);
if (o == NULL) {
- addReply(c,shared.nokeyerr);
+ addReply(c,shared.ok);
} else {
if (o->type != REDIS_LIST) {
addReply(c,shared.wrongtypeerr);
addReply(c,shared.wrongtypeerr);
} else {
s = o->ptr;
- addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",
+ addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",
dictSize(s)));
}
}
return dictSize(*d1)-dictSize(*d2);
}
-static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, robj *dstkey) {
+static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long setsnum, robj *dstkey) {
dict **dv = zmalloc(sizeof(dict*)*setsnum);
dictIterator *di;
dictEntry *de;
robj *lenobj = NULL, *dstset = NULL;
- int j, cardinality = 0;
+ unsigned long j, cardinality = 0;
for (j = 0; j < setsnum; j++) {
robj *setobj;
if (!setobj) {
zfree(dv);
if (dstkey) {
- deleteKey(c->db,dstkey);
- addReply(c,shared.ok);
+ if (deleteKey(c->db,dstkey))
+ server.dirty++;
+ addReply(c,shared.czero);
} else {
addReply(c,shared.nullmultibulk);
}
}
if (!dstkey) {
- lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n",cardinality);
+ lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality);
} else {
- addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",
+ addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",
dictSize((dict*)dstset->ptr)));
server.dirty++;
}
if (!dstkey) {
decrRefCount(dstset);
} else {
- addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",
+ addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",
dictSize((dict*)dstset->ptr)));
server.dirty++;
}
robj *o;
int start = atoi(c->argv[2]->ptr);
int end = atoi(c->argv[3]->ptr);
+ int withscores = 0;
+
+ if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores")) {
+ withscores = 1;
+ } else if (c->argc >= 5) {
+ addReply(c,shared.syntaxerr);
+ return;
+ }
o = lookupKeyRead(c->db,c->argv[1]);
if (o == NULL) {
ln = ln->forward[0];
}
- addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen));
+ addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",
+ withscores ? (rangelen*2) : rangelen));
for (j = 0; j < rangelen; j++) {
ele = ln->obj;
addReplyBulkLen(c,ele);
addReply(c,ele);
addReply(c,shared.crlf);
+ if (withscores)
+ addReplyDouble(c,ln->score);
ln = reverse ? ln->backward : ln->forward[0];
}
}
int offset = 0, limit = -1;
if (c->argc != 4 && c->argc != 7) {
- addReplySds(c,sdsnew("-ERR wrong number of arguments\r\n"));
+ addReplySds(c,
+ sdsnew("-ERR wrong number of arguments for ZRANGEBYSCORE\r\n"));
return;
} else if (c->argc == 7 && strcasecmp(c->argv[4]->ptr,"limit")) {
addReply(c,shared.syntaxerr);
addReply(c,shared.wrongtypeerr);
} else {
zs = o->ptr;
- addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",zs->zsl->length));
+ addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",zs->zsl->length));
}
}
}
/* Lookup the key to sort. It must be of the right types */
sortval = lookupKeyRead(c->db,c->argv[1]);
if (sortval == NULL) {
- addReply(c,shared.nokeyerr);
+ addReply(c,shared.nullmultibulk);
return;
}
if (sortval->type != REDIS_SET && sortval->type != REDIS_LIST &&
"redis_version:%s\r\n"
"arch_bits:%s\r\n"
"multiplexing_api:%s\r\n"
- "uptime_in_seconds:%d\r\n"
- "uptime_in_days:%d\r\n"
+ "uptime_in_seconds:%ld\r\n"
+ "uptime_in_days:%ld\r\n"
"connected_clients:%d\r\n"
"connected_slaves:%d\r\n"
"used_memory:%zu\r\n"
"changes_since_last_save:%lld\r\n"
"bgsave_in_progress:%d\r\n"
- "last_save_time:%d\r\n"
+ "last_save_time:%ld\r\n"
+ "bgrewriteaof_in_progress:%d\r\n"
"total_connections_received:%lld\r\n"
"total_commands_processed:%lld\r\n"
"role:%s\r\n"
server.dirty,
server.bgsavechildpid != -1,
server.lastsave,
+ server.bgrewritechildpid != -1,
server.stat_numconnections,
server.stat_numcommands,
server.masterhost == NULL ? "master" : "slave"
static void infoCommand(redisClient *c) {
sds info = genRedisInfoString();
- addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",sdslen(info)));
+ addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n",
+ (unsigned long)sdslen(info)));
addReplySds(c,info);
addReply(c,shared.crlf);
}
}
server.master = createClient(fd);
server.master->flags |= REDIS_MASTER;
+ server.master->authenticated = 1;
server.replstate = REDIS_REPL_CONNECTED;
return REDIS_OK;
}
char seldb[64];
snprintf(seldb,sizeof(seldb),"%d",dictid);
- buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%d\r\n%s\r\n",
- strlen(seldb),seldb);
+ buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n",
+ (unsigned long)strlen(seldb),seldb);
server.appendseldb = dictid;
}
robj *o = argv[j];
o = getDecodedObject(o);
- buf = sdscatprintf(buf,"$%d\r\n",sdslen(o->ptr));
+ buf = sdscatprintf(buf,"$%lu\r\n",(unsigned long)sdslen(o->ptr));
buf = sdscatlen(buf,o->ptr,sdslen(o->ptr));
buf = sdscatlen(buf,"\r\n",2);
decrRefCount(o);
obj = getDecodedObject(obj);
snprintf(buf,sizeof(buf),"$%ld\r\n",(long)sdslen(obj->ptr));
if (fwrite(buf,strlen(buf),1,fp) == 0) goto err;
- if (fwrite(obj->ptr,sdslen(obj->ptr),1,fp) == 0) goto err;
+ if (sdslen(obj->ptr) && fwrite(obj->ptr,sdslen(obj->ptr),1,fp) == 0)
+ goto err;
if (fwrite("\r\n",2,1,fp) == 0) goto err;
decrRefCount(obj);
return 1;
}
/* Save the expire time */
if (expiretime != -1) {
- char cmd[]="*3\r\n$6\r\nEXPIRE\r\n";
+ char cmd[]="*3\r\n$8\r\nEXPIREAT\r\n";
/* If this key is already expired skip it */
if (expiretime < now) continue;
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
werr:
fclose(fp);
unlink(tmpfile);
- redisLog(REDIS_WARNING,"Write error writing append only fileon disk: %s", strerror(errno));
+ redisLog(REDIS_WARNING,"Write error writing append only file on disk: %s", strerror(errno));
if (di) dictReleaseIterator(di);
return REDIS_ERR;
}
return;
}
if (rewriteAppendOnlyFileBackground() == REDIS_OK) {
- addReply(c,shared.ok);
+ char *status = "+Background append only file rewriting started\r\n";
+ addReplySds(c,sdsnew(status));
} else {
addReply(c,shared.err);
}
}
redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD");
addReply(c,shared.ok);
+ } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
+ emptyDb();
+ if (loadAppendOnlyFile(server.appendfilename) != REDIS_OK) {
+ addReply(c,shared.err);
+ return;
+ }
+ redisLog(REDIS_WARNING,"Append Only File loaded by DEBUG LOADAOF");
+ addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) {
dictEntry *de = dictFind(c->db->dict,c->argv[2]);
robj *key, *val;
val = dictGetEntryVal(de);
addReplySds(c,sdscatprintf(sdsempty(),
"+Key at:%p refcount:%d, value at:%p refcount:%d encoding:%d\r\n",
- key, key->refcount, val, val->refcount, val->encoding));
+ (void*)key, key->refcount, (void*)val, val->refcount,
+ val->encoding));
} else {
addReplySds(c,sdsnew(
"-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|RELOAD]\r\n"));
FILE *fp;
if (fork() != 0) exit(0); /* parent exits */
+ printf("New pid: %d\n", getpid());
setsid(); /* create a new session */
/* Every output goes to /dev/null. If Redis is daemonized but
} else {
redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'");
}
- initServer();
if (server.daemonize) daemonize();
+ initServer();
redisLog(REDIS_NOTICE,"Server started, Redis version " REDIS_VERSION);
#ifdef __linux__
linuxOvercommitMemoryWarning();