mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
Initial implementation of EXPIRE
This commit is contained in:
parent
300827b60d
commit
3305306f09
2
Makefile
2
Makefile
@ -6,7 +6,7 @@ DEBUG?= -g
|
|||||||
CFLAGS?= -std=c99 -pedantic -O2 -Wall -W -DSDS_ABORT_ON_OOM
|
CFLAGS?= -std=c99 -pedantic -O2 -Wall -W -DSDS_ABORT_ON_OOM
|
||||||
CCOPT= $(CFLAGS)
|
CCOPT= $(CFLAGS)
|
||||||
|
|
||||||
OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o
|
OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o fastlz.o
|
||||||
BENCHOBJ = ae.o anet.o benchmark.o sds.o adlist.o zmalloc.o
|
BENCHOBJ = ae.o anet.o benchmark.o sds.o adlist.o zmalloc.o
|
||||||
CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o
|
CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o
|
||||||
|
|
||||||
|
1
TODO
1
TODO
@ -1,4 +1,5 @@
|
|||||||
- GETSET
|
- GETSET
|
||||||
|
- Fix pure-PHP lib for the new protocol
|
||||||
- keys expire
|
- keys expire
|
||||||
- sunion ssub
|
- sunion ssub
|
||||||
- network layer stresser in test in demo
|
- network layer stresser in test in demo
|
||||||
|
4
dict.h
4
dict.h
@ -107,8 +107,8 @@ typedef struct dictIterator {
|
|||||||
|
|
||||||
#define dictGetEntryKey(he) ((he)->key)
|
#define dictGetEntryKey(he) ((he)->key)
|
||||||
#define dictGetEntryVal(he) ((he)->val)
|
#define dictGetEntryVal(he) ((he)->val)
|
||||||
#define dictGetHashTableSize(ht) ((ht)->size)
|
#define dictSlots(ht) ((ht)->size)
|
||||||
#define dictGetHashTableUsed(ht) ((ht)->used)
|
#define dictSize(ht) ((ht)->used)
|
||||||
|
|
||||||
/* API */
|
/* API */
|
||||||
dict *dictCreate(dictType *type, void *privDataPtr);
|
dict *dictCreate(dictType *type, void *privDataPtr);
|
||||||
|
@ -102,6 +102,7 @@ static struct redisCommand cmdTable[] = {
|
|||||||
{"sort",-2,REDIS_CMD_INLINE},
|
{"sort",-2,REDIS_CMD_INLINE},
|
||||||
{"info",1,REDIS_CMD_INLINE},
|
{"info",1,REDIS_CMD_INLINE},
|
||||||
{"mget",-2,REDIS_CMD_INLINE},
|
{"mget",-2,REDIS_CMD_INLINE},
|
||||||
|
{"expire",3,REDIS_CMD_INLINE},
|
||||||
{NULL,0,0}
|
{NULL,0,0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
440
redis.c
440
redis.c
@ -158,11 +158,17 @@ typedef struct redisObject {
|
|||||||
int refcount;
|
int refcount;
|
||||||
} robj;
|
} robj;
|
||||||
|
|
||||||
|
typedef struct redisDb {
|
||||||
|
dict *dict;
|
||||||
|
dict *expires;
|
||||||
|
int id;
|
||||||
|
} redisDb;
|
||||||
|
|
||||||
/* With multiplexing we need to take per-clinet state.
|
/* With multiplexing we need to take per-clinet state.
|
||||||
* Clients are taken in a liked list. */
|
* Clients are taken in a liked list. */
|
||||||
typedef struct redisClient {
|
typedef struct redisClient {
|
||||||
int fd;
|
int fd;
|
||||||
dict *dict;
|
redisDb *db;
|
||||||
int dictid;
|
int dictid;
|
||||||
sds querybuf;
|
sds querybuf;
|
||||||
robj *argv[REDIS_MAX_ARGS];
|
robj *argv[REDIS_MAX_ARGS];
|
||||||
@ -185,7 +191,7 @@ struct saveparam {
|
|||||||
struct redisServer {
|
struct redisServer {
|
||||||
int port;
|
int port;
|
||||||
int fd;
|
int fd;
|
||||||
dict **dict;
|
redisDb *db;
|
||||||
dict *sharingpool;
|
dict *sharingpool;
|
||||||
unsigned int sharingpoolsize;
|
unsigned int sharingpoolsize;
|
||||||
long long dirty; /* changes to DB from the last save */
|
long long dirty; /* changes to DB from the last save */
|
||||||
@ -276,6 +282,9 @@ static robj *createStringObject(char *ptr, size_t len);
|
|||||||
static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int dictid, robj **argv, int argc);
|
static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int dictid, robj **argv, int argc);
|
||||||
static int syncWithMaster(void);
|
static int syncWithMaster(void);
|
||||||
static robj *tryObjectSharing(robj *o);
|
static robj *tryObjectSharing(robj *o);
|
||||||
|
static int removeExpire(redisDb *db, robj *key);
|
||||||
|
static int expireIfNeeded(redisDb *db, robj *key);
|
||||||
|
static int deleteIfVolatile(redisDb *db, robj *key);
|
||||||
|
|
||||||
static void authCommand(redisClient *c);
|
static void authCommand(redisClient *c);
|
||||||
static void pingCommand(redisClient *c);
|
static void pingCommand(redisClient *c);
|
||||||
@ -324,6 +333,7 @@ static void lremCommand(redisClient *c);
|
|||||||
static void infoCommand(redisClient *c);
|
static void infoCommand(redisClient *c);
|
||||||
static void mgetCommand(redisClient *c);
|
static void mgetCommand(redisClient *c);
|
||||||
static void monitorCommand(redisClient *c);
|
static void monitorCommand(redisClient *c);
|
||||||
|
static void expireCommand(redisClient *c);
|
||||||
|
|
||||||
/*================================= Globals ================================= */
|
/*================================= Globals ================================= */
|
||||||
|
|
||||||
@ -378,6 +388,7 @@ static struct redisCommand cmdTable[] = {
|
|||||||
{"sort",sortCommand,-2,REDIS_CMD_INLINE},
|
{"sort",sortCommand,-2,REDIS_CMD_INLINE},
|
||||||
{"info",infoCommand,1,REDIS_CMD_INLINE},
|
{"info",infoCommand,1,REDIS_CMD_INLINE},
|
||||||
{"monitor",monitorCommand,1,REDIS_CMD_INLINE},
|
{"monitor",monitorCommand,1,REDIS_CMD_INLINE},
|
||||||
|
{"expire",expireCommand,3,REDIS_CMD_INLINE},
|
||||||
{NULL,NULL,0,0}
|
{NULL,NULL,0,0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -628,8 +639,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
|||||||
/* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL
|
/* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL
|
||||||
* we resize the hash table to save memory */
|
* we resize the hash table to save memory */
|
||||||
for (j = 0; j < server.dbnum; j++) {
|
for (j = 0; j < server.dbnum; j++) {
|
||||||
size = dictGetHashTableSize(server.dict[j]);
|
size = dictSlots(server.db[j].dict);
|
||||||
used = dictGetHashTableUsed(server.dict[j]);
|
used = dictSize(server.db[j].dict);
|
||||||
if (!(loops % 5) && used > 0) {
|
if (!(loops % 5) && used > 0) {
|
||||||
redisLog(REDIS_DEBUG,"DB %d: %d keys in %d slots HT.",j,used,size);
|
redisLog(REDIS_DEBUG,"DB %d: %d keys in %d slots HT.",j,used,size);
|
||||||
/* dictPrintStats(server.dict); */
|
/* dictPrintStats(server.dict); */
|
||||||
@ -637,7 +648,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
|||||||
if (size && used && size > REDIS_HT_MINSLOTS &&
|
if (size && used && size > REDIS_HT_MINSLOTS &&
|
||||||
(used*100/size < REDIS_HT_MINFILL)) {
|
(used*100/size < REDIS_HT_MINFILL)) {
|
||||||
redisLog(REDIS_NOTICE,"The hash table %d is too sparse, resize it...",j);
|
redisLog(REDIS_NOTICE,"The hash table %d is too sparse, resize it...",j);
|
||||||
dictResize(server.dict[j]);
|
dictResize(server.db[j].dict);
|
||||||
redisLog(REDIS_NOTICE,"Hash table %d resized.",j);
|
redisLog(REDIS_NOTICE,"Hash table %d resized.",j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -648,7 +659,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
|||||||
listLength(server.clients)-listLength(server.slaves),
|
listLength(server.clients)-listLength(server.slaves),
|
||||||
listLength(server.slaves),
|
listLength(server.slaves),
|
||||||
server.usedmemory,
|
server.usedmemory,
|
||||||
dictGetHashTableUsed(server.sharingpool));
|
dictSize(server.sharingpool));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close connections of timedout clients */
|
/* Close connections of timedout clients */
|
||||||
@ -787,18 +798,21 @@ static void initServer() {
|
|||||||
server.objfreelist = listCreate();
|
server.objfreelist = listCreate();
|
||||||
createSharedObjects();
|
createSharedObjects();
|
||||||
server.el = aeCreateEventLoop();
|
server.el = aeCreateEventLoop();
|
||||||
server.dict = zmalloc(sizeof(dict*)*server.dbnum);
|
server.db = zmalloc(sizeof(redisDb)*server.dbnum);
|
||||||
server.sharingpool = dictCreate(&setDictType,NULL);
|
server.sharingpool = dictCreate(&setDictType,NULL);
|
||||||
server.sharingpoolsize = 1024;
|
server.sharingpoolsize = 1024;
|
||||||
if (!server.dict || !server.clients || !server.slaves || !server.monitors || !server.el || !server.objfreelist)
|
if (!server.db || !server.clients || !server.slaves || !server.monitors || !server.el || !server.objfreelist)
|
||||||
oom("server initialization"); /* Fatal OOM */
|
oom("server initialization"); /* Fatal OOM */
|
||||||
server.fd = anetTcpServer(server.neterr, server.port, server.bindaddr);
|
server.fd = anetTcpServer(server.neterr, server.port, server.bindaddr);
|
||||||
if (server.fd == -1) {
|
if (server.fd == -1) {
|
||||||
redisLog(REDIS_WARNING, "Opening TCP port: %s", server.neterr);
|
redisLog(REDIS_WARNING, "Opening TCP port: %s", server.neterr);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
for (j = 0; j < server.dbnum; j++)
|
for (j = 0; j < server.dbnum; j++) {
|
||||||
server.dict[j] = dictCreate(&hashDictType,NULL);
|
server.db[j].dict = dictCreate(&hashDictType,NULL);
|
||||||
|
server.db[j].expires = dictCreate(&setDictType,NULL);
|
||||||
|
server.db[j].id = j;
|
||||||
|
}
|
||||||
server.cronloops = 0;
|
server.cronloops = 0;
|
||||||
server.bgsaveinprogress = 0;
|
server.bgsaveinprogress = 0;
|
||||||
server.lastsave = time(NULL);
|
server.lastsave = time(NULL);
|
||||||
@ -814,8 +828,10 @@ static void initServer() {
|
|||||||
static void emptyDb() {
|
static void emptyDb() {
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
for (j = 0; j < server.dbnum; j++)
|
for (j = 0; j < server.dbnum; j++) {
|
||||||
dictEmpty(server.dict[j]);
|
dictEmpty(server.db[j].dict);
|
||||||
|
dictEmpty(server.db[j].expires);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* I agree, this is a very rudimental way to load a configuration...
|
/* I agree, this is a very rudimental way to load a configuration...
|
||||||
@ -1150,9 +1166,9 @@ static int processCommand(redisClient *c) {
|
|||||||
dirty = server.dirty;
|
dirty = server.dirty;
|
||||||
cmd->proc(c);
|
cmd->proc(c);
|
||||||
if (server.dirty-dirty != 0 && listLength(server.slaves))
|
if (server.dirty-dirty != 0 && listLength(server.slaves))
|
||||||
replicationFeedSlaves(server.slaves,cmd,c->dictid,c->argv,c->argc);
|
replicationFeedSlaves(server.slaves,cmd,c->db->id,c->argv,c->argc);
|
||||||
if (listLength(server.monitors))
|
if (listLength(server.monitors))
|
||||||
replicationFeedSlaves(server.monitors,cmd,c->dictid,c->argv,c->argc);
|
replicationFeedSlaves(server.monitors,cmd,c->db->id,c->argv,c->argc);
|
||||||
server.stat_numcommands++;
|
server.stat_numcommands++;
|
||||||
|
|
||||||
/* Prepare the client for the next command */
|
/* Prepare the client for the next command */
|
||||||
@ -1310,8 +1326,7 @@ again:
|
|||||||
static int selectDb(redisClient *c, int id) {
|
static int selectDb(redisClient *c, int id) {
|
||||||
if (id < 0 || id >= server.dbnum)
|
if (id < 0 || id >= server.dbnum)
|
||||||
return REDIS_ERR;
|
return REDIS_ERR;
|
||||||
c->dict = server.dict[id];
|
c->db = &server.db[id];
|
||||||
c->dictid = id;
|
|
||||||
return REDIS_OK;
|
return REDIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1413,14 +1428,6 @@ static robj *createSetObject(void) {
|
|||||||
return createObject(REDIS_SET,d);
|
return createObject(REDIS_SET,d);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
static robj *createHashObject(void) {
|
|
||||||
dict *d = dictCreate(&hashDictType,NULL);
|
|
||||||
if (!d) oom("dictCreate");
|
|
||||||
return createObject(REDIS_SET,d);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void freeStringObject(robj *o) {
|
static void freeStringObject(robj *o) {
|
||||||
sdsfree(o->ptr);
|
sdsfree(o->ptr);
|
||||||
}
|
}
|
||||||
@ -1462,7 +1469,7 @@ static robj *tryObjectSharing(robj *o) {
|
|||||||
struct dictEntry *de;
|
struct dictEntry *de;
|
||||||
unsigned long c;
|
unsigned long c;
|
||||||
|
|
||||||
if (server.shareobjects == 0) return o;
|
if (o == NULL || server.shareobjects == 0) return o;
|
||||||
|
|
||||||
assert(o->type == REDIS_STRING);
|
assert(o->type == REDIS_STRING);
|
||||||
de = dictFind(server.sharingpool,o);
|
de = dictFind(server.sharingpool,o);
|
||||||
@ -1479,7 +1486,7 @@ static robj *tryObjectSharing(robj *o) {
|
|||||||
* shared we increment its count, everytime there is a miss we
|
* shared we increment its count, everytime there is a miss we
|
||||||
* recrement the counter of a random object. If this object reaches
|
* recrement the counter of a random object. If this object reaches
|
||||||
* zero we remove the object and put the current object instead. */
|
* zero we remove the object and put the current object instead. */
|
||||||
if (dictGetHashTableUsed(server.sharingpool) >=
|
if (dictSize(server.sharingpool) >=
|
||||||
server.sharingpoolsize) {
|
server.sharingpoolsize) {
|
||||||
de = dictGetRandomKey(server.sharingpool);
|
de = dictGetRandomKey(server.sharingpool);
|
||||||
assert(de != NULL);
|
assert(de != NULL);
|
||||||
@ -1502,6 +1509,26 @@ static robj *tryObjectSharing(robj *o) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static robj *lookupKey(redisDb *db, robj *key) {
|
||||||
|
dictEntry *de = dictFind(db->dict,key);
|
||||||
|
return de ? dictGetEntryVal(de) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static robj *lookupKeyRead(redisDb *db, robj *key) {
|
||||||
|
expireIfNeeded(db,key);
|
||||||
|
return lookupKey(db,key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static robj *lookupKeyWrite(redisDb *db, robj *key) {
|
||||||
|
deleteIfVolatile(db,key);
|
||||||
|
return lookupKey(db,key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int deleteKey(redisDb *db, robj *key) {
|
||||||
|
if (dictSize(db->expires)) dictDelete(db->expires,key);
|
||||||
|
return dictDelete(db->dict,key) == DICT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*============================ DB saving/loading ============================ */
|
/*============================ DB saving/loading ============================ */
|
||||||
|
|
||||||
static int rdbSaveType(FILE *fp, unsigned char type) {
|
static int rdbSaveType(FILE *fp, unsigned char type) {
|
||||||
@ -1604,8 +1631,8 @@ static int rdbSave(char *filename) {
|
|||||||
}
|
}
|
||||||
if (fwrite("REDIS0001",9,1,fp) == 0) goto werr;
|
if (fwrite("REDIS0001",9,1,fp) == 0) goto werr;
|
||||||
for (j = 0; j < server.dbnum; j++) {
|
for (j = 0; j < server.dbnum; j++) {
|
||||||
dict *d = server.dict[j];
|
dict *d = server.db[j].dict;
|
||||||
if (dictGetHashTableUsed(d) == 0) continue;
|
if (dictSize(d) == 0) continue;
|
||||||
di = dictGetIterator(d);
|
di = dictGetIterator(d);
|
||||||
if (!di) {
|
if (!di) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@ -1645,7 +1672,7 @@ static int rdbSave(char *filename) {
|
|||||||
dictEntry *de;
|
dictEntry *de;
|
||||||
|
|
||||||
if (!set) oom("dictGetIteraotr");
|
if (!set) oom("dictGetIteraotr");
|
||||||
if (rdbSaveLen(fp,dictGetHashTableUsed(set)) == -1) goto werr;
|
if (rdbSaveLen(fp,dictSize(set)) == -1) goto werr;
|
||||||
while((de = dictNext(di)) != NULL) {
|
while((de = dictNext(di)) != NULL) {
|
||||||
robj *eleobj = dictGetEntryKey(de);
|
robj *eleobj = dictGetEntryKey(de);
|
||||||
|
|
||||||
@ -1785,7 +1812,7 @@ static robj *rdbLoadStringObject(FILE*fp, int rdbver) {
|
|||||||
case REDIS_RDB_ENC_INT8:
|
case REDIS_RDB_ENC_INT8:
|
||||||
case REDIS_RDB_ENC_INT16:
|
case REDIS_RDB_ENC_INT16:
|
||||||
case REDIS_RDB_ENC_INT32:
|
case REDIS_RDB_ENC_INT32:
|
||||||
return rdbLoadIntegerObject(fp,len);
|
return tryObjectSharing(rdbLoadIntegerObject(fp,len));
|
||||||
default:
|
default:
|
||||||
assert(0!=0);
|
assert(0!=0);
|
||||||
}
|
}
|
||||||
@ -1806,7 +1833,7 @@ static int rdbLoad(char *filename) {
|
|||||||
uint32_t dbid;
|
uint32_t dbid;
|
||||||
int type;
|
int type;
|
||||||
int retval;
|
int retval;
|
||||||
dict *d = server.dict[0];
|
dict *d = server.db[0].dict;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
int rdbver;
|
int rdbver;
|
||||||
fp = fopen(filename,"r");
|
fp = fopen(filename,"r");
|
||||||
@ -1838,7 +1865,7 @@ static int rdbLoad(char *filename) {
|
|||||||
redisLog(REDIS_WARNING,"FATAL: Data file was created with a Redis server configured to handle more than %d databases. Exiting\n", server.dbnum);
|
redisLog(REDIS_WARNING,"FATAL: Data file was created with a Redis server configured to handle more than %d databases. Exiting\n", server.dbnum);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
d = server.dict[dbid];
|
d = server.db[dbid].dict;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* Read key */
|
/* Read key */
|
||||||
@ -1916,10 +1943,10 @@ static void echoCommand(redisClient *c) {
|
|||||||
static void setGenericCommand(redisClient *c, int nx) {
|
static void setGenericCommand(redisClient *c, int nx) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
retval = dictAdd(c->dict,c->argv[1],c->argv[2]);
|
retval = dictAdd(c->db->dict,c->argv[1],c->argv[2]);
|
||||||
if (retval == DICT_ERR) {
|
if (retval == DICT_ERR) {
|
||||||
if (!nx) {
|
if (!nx) {
|
||||||
dictReplace(c->dict,c->argv[1],c->argv[2]);
|
dictReplace(c->db->dict,c->argv[1],c->argv[2]);
|
||||||
incrRefCount(c->argv[2]);
|
incrRefCount(c->argv[2]);
|
||||||
} else {
|
} else {
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
@ -1930,6 +1957,7 @@ static void setGenericCommand(redisClient *c, int nx) {
|
|||||||
incrRefCount(c->argv[2]);
|
incrRefCount(c->argv[2]);
|
||||||
}
|
}
|
||||||
server.dirty++;
|
server.dirty++;
|
||||||
|
removeExpire(c->db,c->argv[1]);
|
||||||
addReply(c, nx ? shared.cone : shared.ok);
|
addReply(c, nx ? shared.cone : shared.ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1942,14 +1970,11 @@ static void setnxCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void getCommand(redisClient *c) {
|
static void getCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o = lookupKeyRead(c->db,c->argv[1]);
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
if (o == NULL) {
|
||||||
if (de == NULL) {
|
|
||||||
addReply(c,shared.nullbulk);
|
addReply(c,shared.nullbulk);
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
if (o->type != REDIS_STRING) {
|
if (o->type != REDIS_STRING) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
} else {
|
} else {
|
||||||
@ -1961,17 +1986,14 @@ static void getCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void mgetCommand(redisClient *c) {
|
static void mgetCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-1));
|
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-1));
|
||||||
for (j = 1; j < c->argc; j++) {
|
for (j = 1; j < c->argc; j++) {
|
||||||
de = dictFind(c->dict,c->argv[j]);
|
robj *o = lookupKeyRead(c->db,c->argv[j]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.nullbulk);
|
addReply(c,shared.nullbulk);
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
if (o->type != REDIS_STRING) {
|
if (o->type != REDIS_STRING) {
|
||||||
addReply(c,shared.nullbulk);
|
addReply(c,shared.nullbulk);
|
||||||
} else {
|
} else {
|
||||||
@ -1984,17 +2006,14 @@ static void mgetCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void incrDecrCommand(redisClient *c, int incr) {
|
static void incrDecrCommand(redisClient *c, int incr) {
|
||||||
dictEntry *de;
|
|
||||||
long long value;
|
long long value;
|
||||||
int retval;
|
int retval;
|
||||||
robj *o;
|
robj *o;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
value = 0;
|
value = 0;
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
if (o->type != REDIS_STRING) {
|
if (o->type != REDIS_STRING) {
|
||||||
value = 0;
|
value = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -2006,9 +2025,10 @@ static void incrDecrCommand(redisClient *c, int incr) {
|
|||||||
|
|
||||||
value += incr;
|
value += incr;
|
||||||
o = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
|
o = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
|
||||||
retval = dictAdd(c->dict,c->argv[1],o);
|
retval = dictAdd(c->db->dict,c->argv[1],o);
|
||||||
if (retval == DICT_ERR) {
|
if (retval == DICT_ERR) {
|
||||||
dictReplace(c->dict,c->argv[1],o);
|
dictReplace(c->db->dict,c->argv[1],o);
|
||||||
|
removeExpire(c->db,c->argv[1]);
|
||||||
} else {
|
} else {
|
||||||
incrRefCount(c->argv[1]);
|
incrRefCount(c->argv[1]);
|
||||||
}
|
}
|
||||||
@ -2039,7 +2059,7 @@ static void decrbyCommand(redisClient *c) {
|
|||||||
/* ========================= Type agnostic commands ========================= */
|
/* ========================= Type agnostic commands ========================= */
|
||||||
|
|
||||||
static void delCommand(redisClient *c) {
|
static void delCommand(redisClient *c) {
|
||||||
if (dictDelete(c->dict,c->argv[1]) == DICT_OK) {
|
if (deleteKey(c->db,c->argv[1])) {
|
||||||
server.dirty++;
|
server.dirty++;
|
||||||
addReply(c,shared.cone);
|
addReply(c,shared.cone);
|
||||||
} else {
|
} else {
|
||||||
@ -2048,13 +2068,7 @@ static void delCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void existsCommand(redisClient *c) {
|
static void existsCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
addReply(c,lookupKeyRead(c->db,c->argv[1]) ? shared.cone : shared.czero);
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
|
||||||
if (de == NULL)
|
|
||||||
addReply(c,shared.czero);
|
|
||||||
else
|
|
||||||
addReply(c,shared.cone);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void selectCommand(redisClient *c) {
|
static void selectCommand(redisClient *c) {
|
||||||
@ -2069,8 +2083,11 @@ static void selectCommand(redisClient *c) {
|
|||||||
|
|
||||||
static void randomkeyCommand(redisClient *c) {
|
static void randomkeyCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
dictEntry *de;
|
||||||
|
|
||||||
de = dictGetRandomKey(c->dict);
|
while(1) {
|
||||||
|
de = dictGetRandomKey(c->db->dict);
|
||||||
|
if (expireIfNeeded(c->db,dictGetEntryKey(de)) == 0) break;
|
||||||
|
}
|
||||||
if (de == NULL) {
|
if (de == NULL) {
|
||||||
addReply(c,shared.crlf);
|
addReply(c,shared.crlf);
|
||||||
} else {
|
} else {
|
||||||
@ -2088,20 +2105,23 @@ static void keysCommand(redisClient *c) {
|
|||||||
int numkeys = 0, keyslen = 0;
|
int numkeys = 0, keyslen = 0;
|
||||||
robj *lenobj = createObject(REDIS_STRING,NULL);
|
robj *lenobj = createObject(REDIS_STRING,NULL);
|
||||||
|
|
||||||
di = dictGetIterator(c->dict);
|
di = dictGetIterator(c->db->dict);
|
||||||
if (!di) oom("dictGetIterator");
|
if (!di) oom("dictGetIterator");
|
||||||
addReply(c,lenobj);
|
addReply(c,lenobj);
|
||||||
decrRefCount(lenobj);
|
decrRefCount(lenobj);
|
||||||
while((de = dictNext(di)) != NULL) {
|
while((de = dictNext(di)) != NULL) {
|
||||||
robj *keyobj = dictGetEntryKey(de);
|
robj *keyobj = dictGetEntryKey(de);
|
||||||
|
|
||||||
sds key = keyobj->ptr;
|
sds key = keyobj->ptr;
|
||||||
if ((pattern[0] == '*' && pattern[1] == '\0') ||
|
if ((pattern[0] == '*' && pattern[1] == '\0') ||
|
||||||
stringmatchlen(pattern,plen,key,sdslen(key),0)) {
|
stringmatchlen(pattern,plen,key,sdslen(key),0)) {
|
||||||
if (numkeys != 0)
|
if (expireIfNeeded(c->db,keyobj) == 0) {
|
||||||
addReply(c,shared.space);
|
if (numkeys != 0)
|
||||||
addReply(c,keyobj);
|
addReply(c,shared.space);
|
||||||
numkeys++;
|
addReply(c,keyobj);
|
||||||
keyslen += sdslen(key);
|
numkeys++;
|
||||||
|
keyslen += sdslen(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dictReleaseIterator(di);
|
dictReleaseIterator(di);
|
||||||
@ -2111,7 +2131,7 @@ static void keysCommand(redisClient *c) {
|
|||||||
|
|
||||||
static void dbsizeCommand(redisClient *c) {
|
static void dbsizeCommand(redisClient *c) {
|
||||||
addReplySds(c,
|
addReplySds(c,
|
||||||
sdscatprintf(sdsempty(),":%lu\r\n",dictGetHashTableUsed(c->dict)));
|
sdscatprintf(sdsempty(),":%lu\r\n",dictSize(c->db->dict)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lastsaveCommand(redisClient *c) {
|
static void lastsaveCommand(redisClient *c) {
|
||||||
@ -2120,15 +2140,13 @@ static void lastsaveCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void typeCommand(redisClient *c) {
|
static void typeCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
char *type;
|
char *type;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyRead(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
type = "+none";
|
type = "+none";
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
switch(o->type) {
|
switch(o->type) {
|
||||||
case REDIS_STRING: type = "+string"; break;
|
case REDIS_STRING: type = "+string"; break;
|
||||||
case REDIS_LIST: type = "+list"; break;
|
case REDIS_LIST: type = "+list"; break;
|
||||||
@ -2175,7 +2193,6 @@ static void shutdownCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void renameGenericCommand(redisClient *c, int nx) {
|
static void renameGenericCommand(redisClient *c, int nx) {
|
||||||
dictEntry *de;
|
|
||||||
robj *o;
|
robj *o;
|
||||||
|
|
||||||
/* To use the same key as src and dst is probably an error */
|
/* To use the same key as src and dst is probably an error */
|
||||||
@ -2184,24 +2201,24 @@ static void renameGenericCommand(redisClient *c, int nx) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.nokeyerr);
|
addReply(c,shared.nokeyerr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
o = dictGetEntryVal(de);
|
|
||||||
incrRefCount(o);
|
incrRefCount(o);
|
||||||
if (dictAdd(c->dict,c->argv[2],o) == DICT_ERR) {
|
deleteIfVolatile(c->db,c->argv[2]);
|
||||||
|
if (dictAdd(c->db->dict,c->argv[2],o) == DICT_ERR) {
|
||||||
if (nx) {
|
if (nx) {
|
||||||
decrRefCount(o);
|
decrRefCount(o);
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dictReplace(c->dict,c->argv[2],o);
|
dictReplace(c->db->dict,c->argv[2],o);
|
||||||
} else {
|
} else {
|
||||||
incrRefCount(c->argv[2]);
|
incrRefCount(c->argv[2]);
|
||||||
}
|
}
|
||||||
dictDelete(c->dict,c->argv[1]);
|
deleteKey(c->db,c->argv[1]);
|
||||||
server.dirty++;
|
server.dirty++;
|
||||||
addReply(c,nx ? shared.cone : shared.ok);
|
addReply(c,nx ? shared.cone : shared.ok);
|
||||||
}
|
}
|
||||||
@ -2215,21 +2232,19 @@ static void renamenxCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void moveCommand(redisClient *c) {
|
static void moveCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
robj *o, *key;
|
redisDb *src, *dst;
|
||||||
dict *src, *dst;
|
|
||||||
int srcid;
|
int srcid;
|
||||||
|
|
||||||
/* Obtain source and target DB pointers */
|
/* Obtain source and target DB pointers */
|
||||||
src = c->dict;
|
src = c->db;
|
||||||
srcid = c->dictid;
|
srcid = c->db->id;
|
||||||
if (selectDb(c,atoi(c->argv[2]->ptr)) == REDIS_ERR) {
|
if (selectDb(c,atoi(c->argv[2]->ptr)) == REDIS_ERR) {
|
||||||
addReply(c,shared.outofrangeerr);
|
addReply(c,shared.outofrangeerr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dst = c->dict;
|
dst = c->db;
|
||||||
c->dict = src;
|
selectDb(c,srcid); /* Back to the source DB */
|
||||||
c->dictid = srcid;
|
|
||||||
|
|
||||||
/* If the user is moving using as target the same
|
/* If the user is moving using as target the same
|
||||||
* DB as the source DB it is probably an error. */
|
* DB as the source DB it is probably an error. */
|
||||||
@ -2239,24 +2254,23 @@ static void moveCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the element exists and get a reference */
|
/* Check if the element exists and get a reference */
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (!de) {
|
if (!o) {
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to add the element to the target DB */
|
/* Try to add the element to the target DB */
|
||||||
key = dictGetEntryKey(de);
|
deleteIfVolatile(dst,c->argv[1]);
|
||||||
o = dictGetEntryVal(de);
|
if (dictAdd(dst->dict,c->argv[1],o) == DICT_ERR) {
|
||||||
if (dictAdd(dst,key,o) == DICT_ERR) {
|
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
incrRefCount(key);
|
incrRefCount(c->argv[1]);
|
||||||
incrRefCount(o);
|
incrRefCount(o);
|
||||||
|
|
||||||
/* OK! key moved, free the entry in the source DB */
|
/* OK! key moved, free the entry in the source DB */
|
||||||
dictDelete(src,c->argv[1]);
|
deleteKey(src,c->argv[1]);
|
||||||
server.dirty++;
|
server.dirty++;
|
||||||
addReply(c,shared.cone);
|
addReply(c,shared.cone);
|
||||||
}
|
}
|
||||||
@ -2264,11 +2278,10 @@ static void moveCommand(redisClient *c) {
|
|||||||
/* =================================== Lists ================================ */
|
/* =================================== Lists ================================ */
|
||||||
static void pushGenericCommand(redisClient *c, int where) {
|
static void pushGenericCommand(redisClient *c, int where) {
|
||||||
robj *lobj;
|
robj *lobj;
|
||||||
dictEntry *de;
|
|
||||||
list *list;
|
list *list;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
lobj = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (lobj == NULL) {
|
||||||
lobj = createListObject();
|
lobj = createListObject();
|
||||||
list = lobj->ptr;
|
list = lobj->ptr;
|
||||||
if (where == REDIS_HEAD) {
|
if (where == REDIS_HEAD) {
|
||||||
@ -2276,11 +2289,10 @@ static void pushGenericCommand(redisClient *c, int where) {
|
|||||||
} else {
|
} else {
|
||||||
if (!listAddNodeTail(list,c->argv[2])) oom("listAddNodeTail");
|
if (!listAddNodeTail(list,c->argv[2])) oom("listAddNodeTail");
|
||||||
}
|
}
|
||||||
dictAdd(c->dict,c->argv[1],lobj);
|
dictAdd(c->db->dict,c->argv[1],lobj);
|
||||||
incrRefCount(c->argv[1]);
|
incrRefCount(c->argv[1]);
|
||||||
incrRefCount(c->argv[2]);
|
incrRefCount(c->argv[2]);
|
||||||
} else {
|
} else {
|
||||||
lobj = dictGetEntryVal(de);
|
|
||||||
if (lobj->type != REDIS_LIST) {
|
if (lobj->type != REDIS_LIST) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
return;
|
return;
|
||||||
@ -2306,15 +2318,14 @@ static void rpushCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void llenCommand(redisClient *c) {
|
static void llenCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
list *l;
|
list *l;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyRead(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
if (o->type != REDIS_LIST) {
|
if (o->type != REDIS_LIST) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
} else {
|
} else {
|
||||||
@ -2325,15 +2336,13 @@ static void llenCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void lindexCommand(redisClient *c) {
|
static void lindexCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
int index = atoi(c->argv[2]->ptr);
|
int index = atoi(c->argv[2]->ptr);
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyRead(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.nullbulk);
|
addReply(c,shared.nullbulk);
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
if (o->type != REDIS_LIST) {
|
if (o->type != REDIS_LIST) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
} else {
|
} else {
|
||||||
@ -2354,15 +2363,13 @@ static void lindexCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void lsetCommand(redisClient *c) {
|
static void lsetCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
int index = atoi(c->argv[2]->ptr);
|
int index = atoi(c->argv[2]->ptr);
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.nokeyerr);
|
addReply(c,shared.nokeyerr);
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
if (o->type != REDIS_LIST) {
|
if (o->type != REDIS_LIST) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
} else {
|
} else {
|
||||||
@ -2386,14 +2393,12 @@ static void lsetCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void popGenericCommand(redisClient *c, int where) {
|
static void popGenericCommand(redisClient *c, int where) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.nullbulk);
|
addReply(c,shared.nullbulk);
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
if (o->type != REDIS_LIST) {
|
if (o->type != REDIS_LIST) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
} else {
|
} else {
|
||||||
@ -2428,16 +2433,14 @@ static void rpopCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void lrangeCommand(redisClient *c) {
|
static void lrangeCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
int start = atoi(c->argv[2]->ptr);
|
int start = atoi(c->argv[2]->ptr);
|
||||||
int end = atoi(c->argv[3]->ptr);
|
int end = atoi(c->argv[3]->ptr);
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyRead(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.nullmultibulk);
|
addReply(c,shared.nullmultibulk);
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
if (o->type != REDIS_LIST) {
|
if (o->type != REDIS_LIST) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
} else {
|
} else {
|
||||||
@ -2477,16 +2480,14 @@ static void lrangeCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void ltrimCommand(redisClient *c) {
|
static void ltrimCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
int start = atoi(c->argv[2]->ptr);
|
int start = atoi(c->argv[2]->ptr);
|
||||||
int end = atoi(c->argv[3]->ptr);
|
int end = atoi(c->argv[3]->ptr);
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.nokeyerr);
|
addReply(c,shared.nokeyerr);
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
if (o->type != REDIS_LIST) {
|
if (o->type != REDIS_LIST) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
} else {
|
} else {
|
||||||
@ -2528,14 +2529,12 @@ static void ltrimCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void lremCommand(redisClient *c) {
|
static void lremCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.nokeyerr);
|
addReply(c,shared.nokeyerr);
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
if (o->type != REDIS_LIST) {
|
if (o->type != REDIS_LIST) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
} else {
|
} else {
|
||||||
@ -2570,16 +2569,14 @@ static void lremCommand(redisClient *c) {
|
|||||||
/* ==================================== Sets ================================ */
|
/* ==================================== Sets ================================ */
|
||||||
|
|
||||||
static void saddCommand(redisClient *c) {
|
static void saddCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
|
||||||
robj *set;
|
robj *set;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
set = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (set == NULL) {
|
||||||
set = createSetObject();
|
set = createSetObject();
|
||||||
dictAdd(c->dict,c->argv[1],set);
|
dictAdd(c->db->dict,c->argv[1],set);
|
||||||
incrRefCount(c->argv[1]);
|
incrRefCount(c->argv[1]);
|
||||||
} else {
|
} else {
|
||||||
set = dictGetEntryVal(de);
|
|
||||||
if (set->type != REDIS_SET) {
|
if (set->type != REDIS_SET) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
return;
|
return;
|
||||||
@ -2595,15 +2592,12 @@ static void saddCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void sremCommand(redisClient *c) {
|
static void sremCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *set;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
set = lookupKeyWrite(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (set == NULL) {
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
} else {
|
} else {
|
||||||
robj *set;
|
|
||||||
|
|
||||||
set = dictGetEntryVal(de);
|
|
||||||
if (set->type != REDIS_SET) {
|
if (set->type != REDIS_SET) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
return;
|
return;
|
||||||
@ -2618,15 +2612,12 @@ static void sremCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void sismemberCommand(redisClient *c) {
|
static void sismemberCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *set;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
set = lookupKeyRead(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (set == NULL) {
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
} else {
|
} else {
|
||||||
robj *set;
|
|
||||||
|
|
||||||
set = dictGetEntryVal(de);
|
|
||||||
if (set->type != REDIS_SET) {
|
if (set->type != REDIS_SET) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
return;
|
return;
|
||||||
@ -2639,21 +2630,20 @@ static void sismemberCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void scardCommand(redisClient *c) {
|
static void scardCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
robj *o;
|
||||||
dict *s;
|
dict *s;
|
||||||
|
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
o = lookupKeyRead(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (o == NULL) {
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
robj *o = dictGetEntryVal(de);
|
|
||||||
if (o->type != REDIS_SET) {
|
if (o->type != REDIS_SET) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
} else {
|
} else {
|
||||||
s = o->ptr;
|
s = o->ptr;
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",
|
addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",
|
||||||
dictGetHashTableUsed(s)));
|
dictSize(s)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2661,7 +2651,7 @@ static void scardCommand(redisClient *c) {
|
|||||||
static int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
|
static int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
|
||||||
dict **d1 = (void*) s1, **d2 = (void*) s2;
|
dict **d1 = (void*) s1, **d2 = (void*) s2;
|
||||||
|
|
||||||
return dictGetHashTableUsed(*d1)-dictGetHashTableUsed(*d2);
|
return dictSize(*d1)-dictSize(*d2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, robj *dstkey) {
|
static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, robj *dstkey) {
|
||||||
@ -2674,15 +2664,15 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
|
|||||||
if (!dv) oom("sinterCommand");
|
if (!dv) oom("sinterCommand");
|
||||||
for (j = 0; j < setsnum; j++) {
|
for (j = 0; j < setsnum; j++) {
|
||||||
robj *setobj;
|
robj *setobj;
|
||||||
dictEntry *de;
|
|
||||||
|
setobj = dstkey ?
|
||||||
de = dictFind(c->dict,setskeys[j]);
|
lookupKeyWrite(c->db,setskeys[j]) :
|
||||||
if (!de) {
|
lookupKeyRead(c->db,setskeys[j]);
|
||||||
|
if (!setobj) {
|
||||||
zfree(dv);
|
zfree(dv);
|
||||||
addReply(c,shared.nokeyerr);
|
addReply(c,shared.nokeyerr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setobj = dictGetEntryVal(de);
|
|
||||||
if (setobj->type != REDIS_SET) {
|
if (setobj->type != REDIS_SET) {
|
||||||
zfree(dv);
|
zfree(dv);
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
@ -2707,8 +2697,8 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
|
|||||||
/* If we have a target key where to store the resulting set
|
/* If we have a target key where to store the resulting set
|
||||||
* create this key with an empty set inside */
|
* create this key with an empty set inside */
|
||||||
dstset = createSetObject();
|
dstset = createSetObject();
|
||||||
dictDelete(c->dict,dstkey);
|
deleteKey(c->db,dstkey);
|
||||||
dictAdd(c->dict,dstkey,dstset);
|
dictAdd(c->db->dict,dstkey,dstset);
|
||||||
incrRefCount(dstkey);
|
incrRefCount(dstkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2754,7 +2744,8 @@ static void sinterstoreCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void flushdbCommand(redisClient *c) {
|
static void flushdbCommand(redisClient *c) {
|
||||||
dictEmpty(c->dict);
|
dictEmpty(c->db->dict);
|
||||||
|
dictEmpty(c->db->expires);
|
||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
rdbSave(server.dbfilename);
|
rdbSave(server.dbfilename);
|
||||||
}
|
}
|
||||||
@ -2775,12 +2766,11 @@ redisSortOperation *createSortOperation(int type, robj *pattern) {
|
|||||||
|
|
||||||
/* Return the value associated to the key with a name obtained
|
/* Return the value associated to the key with a name obtained
|
||||||
* substituting the first occurence of '*' in 'pattern' with 'subst' */
|
* substituting the first occurence of '*' in 'pattern' with 'subst' */
|
||||||
robj *lookupKeyByPattern(dict *dict, robj *pattern, robj *subst) {
|
robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
|
||||||
char *p;
|
char *p;
|
||||||
sds spat, ssub;
|
sds spat, ssub;
|
||||||
robj keyobj;
|
robj keyobj;
|
||||||
int prefixlen, sublen, postfixlen;
|
int prefixlen, sublen, postfixlen;
|
||||||
dictEntry *de;
|
|
||||||
/* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */
|
/* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */
|
||||||
struct {
|
struct {
|
||||||
long len;
|
long len;
|
||||||
@ -2788,7 +2778,6 @@ robj *lookupKeyByPattern(dict *dict, robj *pattern, robj *subst) {
|
|||||||
char buf[REDIS_SORTKEY_MAX+1];
|
char buf[REDIS_SORTKEY_MAX+1];
|
||||||
} keyname;
|
} keyname;
|
||||||
|
|
||||||
|
|
||||||
spat = pattern->ptr;
|
spat = pattern->ptr;
|
||||||
ssub = subst->ptr;
|
ssub = subst->ptr;
|
||||||
if (sdslen(spat)+sdslen(ssub)-1 > REDIS_SORTKEY_MAX) return NULL;
|
if (sdslen(spat)+sdslen(ssub)-1 > REDIS_SORTKEY_MAX) return NULL;
|
||||||
@ -2808,10 +2797,8 @@ robj *lookupKeyByPattern(dict *dict, robj *pattern, robj *subst) {
|
|||||||
keyobj.type = REDIS_STRING;
|
keyobj.type = REDIS_STRING;
|
||||||
keyobj.ptr = ((char*)&keyname)+(sizeof(long)*2);
|
keyobj.ptr = ((char*)&keyname)+(sizeof(long)*2);
|
||||||
|
|
||||||
de = dictFind(dict,&keyobj);
|
|
||||||
/* printf("lookup '%s' => %p\n", keyname.buf,de); */
|
/* printf("lookup '%s' => %p\n", keyname.buf,de); */
|
||||||
if (!de) return NULL;
|
return lookupKeyRead(db,&keyobj);
|
||||||
return dictGetEntryVal(de);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
|
/* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
|
||||||
@ -2856,7 +2843,6 @@ static int sortCompare(const void *s1, const void *s2) {
|
|||||||
/* The SORT command is the most complex command in Redis. Warning: this code
|
/* The SORT command is the most complex command in Redis. Warning: this code
|
||||||
* is optimized for speed and a bit less for readability */
|
* is optimized for speed and a bit less for readability */
|
||||||
static void sortCommand(redisClient *c) {
|
static void sortCommand(redisClient *c) {
|
||||||
dictEntry *de;
|
|
||||||
list *operations;
|
list *operations;
|
||||||
int outputlen = 0;
|
int outputlen = 0;
|
||||||
int desc = 0, alpha = 0;
|
int desc = 0, alpha = 0;
|
||||||
@ -2867,12 +2853,11 @@ static void sortCommand(redisClient *c) {
|
|||||||
redisSortObject *vector; /* Resulting vector to sort */
|
redisSortObject *vector; /* Resulting vector to sort */
|
||||||
|
|
||||||
/* Lookup the key to sort. It must be of the right types */
|
/* Lookup the key to sort. It must be of the right types */
|
||||||
de = dictFind(c->dict,c->argv[1]);
|
sortval = lookupKeyRead(c->db,c->argv[1]);
|
||||||
if (de == NULL) {
|
if (sortval == NULL) {
|
||||||
addReply(c,shared.nokeyerr);
|
addReply(c,shared.nokeyerr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sortval = dictGetEntryVal(de);
|
|
||||||
if (sortval->type != REDIS_SET && sortval->type != REDIS_LIST) {
|
if (sortval->type != REDIS_SET && sortval->type != REDIS_LIST) {
|
||||||
addReply(c,shared.wrongtypeerr);
|
addReply(c,shared.wrongtypeerr);
|
||||||
return;
|
return;
|
||||||
@ -2937,7 +2922,7 @@ static void sortCommand(redisClient *c) {
|
|||||||
/* Load the sorting vector with all the objects to sort */
|
/* Load the sorting vector with all the objects to sort */
|
||||||
vectorlen = (sortval->type == REDIS_LIST) ?
|
vectorlen = (sortval->type == REDIS_LIST) ?
|
||||||
listLength((list*)sortval->ptr) :
|
listLength((list*)sortval->ptr) :
|
||||||
dictGetHashTableUsed((dict*)sortval->ptr);
|
dictSize((dict*)sortval->ptr);
|
||||||
vector = zmalloc(sizeof(redisSortObject)*vectorlen);
|
vector = zmalloc(sizeof(redisSortObject)*vectorlen);
|
||||||
if (!vector) oom("allocating objects vector for SORT");
|
if (!vector) oom("allocating objects vector for SORT");
|
||||||
j = 0;
|
j = 0;
|
||||||
@ -2975,7 +2960,7 @@ static void sortCommand(redisClient *c) {
|
|||||||
if (sortby) {
|
if (sortby) {
|
||||||
robj *byval;
|
robj *byval;
|
||||||
|
|
||||||
byval = lookupKeyByPattern(c->dict,sortby,vector[j].obj);
|
byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);
|
||||||
if (!byval || byval->type != REDIS_STRING) continue;
|
if (!byval || byval->type != REDIS_STRING) continue;
|
||||||
if (alpha) {
|
if (alpha) {
|
||||||
vector[j].u.cmpobj = byval;
|
vector[j].u.cmpobj = byval;
|
||||||
@ -3020,7 +3005,7 @@ static void sortCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
while(ln) {
|
while(ln) {
|
||||||
redisSortOperation *sop = ln->value;
|
redisSortOperation *sop = ln->value;
|
||||||
robj *val = lookupKeyByPattern(c->dict,sop->pattern,
|
robj *val = lookupKeyByPattern(c->db,sop->pattern,
|
||||||
vector[j].obj);
|
vector[j].obj);
|
||||||
|
|
||||||
if (sop->type == REDIS_SORT_GET) {
|
if (sop->type == REDIS_SORT_GET) {
|
||||||
@ -3080,6 +3065,85 @@ static void infoCommand(redisClient *c) {
|
|||||||
addReply(c,shared.crlf);
|
addReply(c,shared.crlf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void monitorCommand(redisClient *c) {
|
||||||
|
/* ignore MONITOR if aleady slave or in monitor mode */
|
||||||
|
if (c->flags & REDIS_SLAVE) return;
|
||||||
|
|
||||||
|
c->flags |= (REDIS_SLAVE|REDIS_MONITOR);
|
||||||
|
c->slaveseldb = 0;
|
||||||
|
if (!listAddNodeTail(server.monitors,c)) oom("listAddNodeTail");
|
||||||
|
addReply(c,shared.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================= Expire ================================= */
|
||||||
|
static int removeExpire(redisDb *db, robj *key) {
|
||||||
|
if (dictDelete(db->expires,key) == DICT_OK) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setExpire(redisDb *db, robj *key, time_t when) {
|
||||||
|
if (dictAdd(db->expires,key,(void*)when) == DICT_ERR) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
incrRefCount(key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int expireIfNeeded(redisDb *db, robj *key) {
|
||||||
|
time_t when;
|
||||||
|
dictEntry *de;
|
||||||
|
|
||||||
|
/* No expire? return ASAP */
|
||||||
|
if (dictSize(db->expires) == 0 ||
|
||||||
|
(de = dictFind(db->expires,key)) == NULL) return 0;
|
||||||
|
|
||||||
|
/* Lookup the expire */
|
||||||
|
when = (time_t) dictGetEntryVal(de);
|
||||||
|
if (time(NULL) <= when) return 0;
|
||||||
|
|
||||||
|
/* Delete the key */
|
||||||
|
dictDelete(db->expires,key);
|
||||||
|
return dictDelete(db->dict,key) == DICT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int deleteIfVolatile(redisDb *db, robj *key) {
|
||||||
|
dictEntry *de;
|
||||||
|
|
||||||
|
/* No expire? return ASAP */
|
||||||
|
if (dictSize(db->expires) == 0 ||
|
||||||
|
(de = dictFind(db->expires,key)) == NULL) return 0;
|
||||||
|
|
||||||
|
/* Delete the key */
|
||||||
|
dictDelete(db->expires,key);
|
||||||
|
return dictDelete(db->dict,key) == DICT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expireCommand(redisClient *c) {
|
||||||
|
dictEntry *de;
|
||||||
|
int seconds = atoi(c->argv[2]->ptr);
|
||||||
|
|
||||||
|
de = dictFind(c->db->dict,c->argv[1]);
|
||||||
|
if (de == NULL) {
|
||||||
|
addReply(c,shared.czero);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (seconds <= 0) {
|
||||||
|
addReply(c, shared.czero);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
time_t when = time(NULL)+seconds;
|
||||||
|
if (setExpire(c->db,c->argv[1],when))
|
||||||
|
addReply(c,shared.cone);
|
||||||
|
else
|
||||||
|
addReply(c,shared.czero);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* =============================== Replication ============================= */
|
/* =============================== Replication ============================= */
|
||||||
|
|
||||||
/* Send the whole output buffer syncronously to the slave. This a general operation in theory, but it is actually useful only for replication. */
|
/* Send the whole output buffer syncronously to the slave. This a general operation in theory, but it is actually useful only for replication. */
|
||||||
@ -3280,16 +3344,6 @@ static int syncWithMaster(void) {
|
|||||||
return REDIS_OK;
|
return REDIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void monitorCommand(redisClient *c) {
|
|
||||||
/* ignore MONITOR if aleady slave or in monitor mode */
|
|
||||||
if (c->flags & REDIS_SLAVE) return;
|
|
||||||
|
|
||||||
c->flags |= (REDIS_SLAVE|REDIS_MONITOR);
|
|
||||||
c->slaveseldb = 0;
|
|
||||||
if (!listAddNodeTail(server.monitors,c)) oom("listAddNodeTail");
|
|
||||||
addReply(c,shared.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =================================== Main! ================================ */
|
/* =================================== Main! ================================ */
|
||||||
|
|
||||||
static void daemonize(void) {
|
static void daemonize(void) {
|
||||||
|
Loading…
Reference in New Issue
Block a user