//当key存在时则push,PUSHX,INSERT命令的底层实现 void pushxGenericCommand(client *c, robj *refval, robj *val, int where) { robj *subject; listTypeIterator *iter; listTypeEntry entry; int inserted = 0; //以写操作读取key对象的value //如果读取失败或读取的value对象不是列表类型则返回 if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,subject,OBJ_LIST)) return; //寻找基准值refval if (refval != NULL) { /* Seek refval from head to tail */ //创建一个列表的迭代器 iter = listTypeInitIterator(subject,0,LIST_TAIL); //将指向当前的entry节点保存到列表类型的entry中,然后指向下一个entry节点 while (listTypeNext(iter,&entry)) { //当前的entry节点的值与基准值refval是否相等 if (listTypeEqual(&entry,refval)) { //如果相等,根据where插入val对象 listTypeInsert(&entry,val,where); inserted = 1; //设置插入的标识,跳出循环 break; } } //事项迭代器 listTypeReleaseIterator(iter); //如果插入成功,键值被修改,则发送信号并且发送"linsert"时间通知 if (inserted) { signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_LIST,"linsert", c->argv[1],c->db->id); server.dirty++; //更新脏键 } else { /* Notify client of a failed insert */ //如果没有插入,则发送插入失败的信息 addReply(c,shared.cnegone); return; } //如果基准值为空 } else { //根据where判断出事件名称 char *event = (where == LIST_HEAD) ? "lpush" : "rpush"; //将val对象推入到列表的头部或尾部 listTypePush(subject,val,where); //当数据库的键被改动,则会调用该函数发送信号 signalModifiedKey(c->db,c->argv[1]); //发送事件通知 notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id); server.dirty++; //更新脏键 } //将插入val后的列表的元素个数发送给client addReplyLongLong(c,listTypeLength(subject)); }
void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) { robj *subject; listTypeIterator *iter; listTypeEntry entry; int inserted = 0; int slotnum = keyHashSlot(c->argv[1]->ptr, sdslen(c->argv[1]->ptr)); if ((subject = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,subject,REDIS_LIST)) return; if (refval != NULL) { /* We're not sure if this value can be inserted yet, but we cannot * convert the list inside the iterator. We don't want to loop over * the list twice (once to see if the value can be inserted and once * to do the actual insert), so we assume this value can be inserted * and convert the ziplist to a regular list if necessary. */ listTypeTryConversion(subject,val); /* Seek refval from head to tail */ iter = listTypeInitIterator(subject,0,REDIS_TAIL); while (listTypeNext(iter,&entry)) { if (listTypeEqual(&entry,refval)) { listTypeInsert(&entry,val,where); inserted = 1; break; } } listTypeReleaseIterator(iter); if (inserted) { /* Check if the length exceeds the ziplist length threshold. */ if (subject->encoding == REDIS_ENCODING_ZIPLIST && ziplistLen(subject->ptr) > server.list_max_ziplist_entries) listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); signalModifiedKey(c->db,c->argv[1],slotnum); notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"linsert", c->argv[1],c->db->id); server.dirty++; } else { /* Notify client of a failed insert */ addReply(c,shared.cnegone); return; } } else { char *event = (where == REDIS_HEAD) ? "lpush" : "rpush"; listTypePush(subject,val,where); signalModifiedKey(c->db,c->argv[1],slotnum); notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id); server.dirty++; } addReplyLongLong(c,listTypeLength(subject)); }
void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) { robj *subject; listTypeIterator *iter; listTypeEntry entry; int inserted = 0; if ((subject = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,subject,REDIS_LIST)) return; if (refval != NULL) { /* Note: we expect refval to be string-encoded because it is *not* the * last argument of the multi-bulk LINSERT. */ redisAssertWithInfo(c,refval,refval->encoding == REDIS_ENCODING_RAW); /* We're not sure if this value can be inserted yet, but we cannot * convert the list inside the iterator. We don't want to loop over * the list twice (once to see if the value can be inserted and once * to do the actual insert), so we assume this value can be inserted * and convert the ziplist to a regular list if necessary. */ listTypeTryConversion(subject,val); /* Seek refval from head to tail */ iter = listTypeInitIterator(subject,0,REDIS_TAIL); while (listTypeNext(iter,&entry)) { if (listTypeEqual(&entry,refval)) { listTypeInsert(&entry,val,where); inserted = 1; break; } } listTypeReleaseIterator(iter); if (inserted) { /* Check if the length exceeds the ziplist length threshold. */ if (subject->encoding == REDIS_ENCODING_ZIPLIST && ziplistLen(subject->ptr) > server.list_max_ziplist_entries) listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); signalModifiedKey(c->db,c->argv[1]); server.dirty++; } else { /* Notify client of a failed insert */ addReply(c,shared.cnegone); return; } } else { listTypePush(subject,val,where); signalModifiedKey(c->db,c->argv[1]); server.dirty++; } addReplyLongLong(c,listTypeLength(subject)); }
void lremCommand(redisClient *c) { robj *subject, *obj; obj = c->argv[3] = tryObjectEncoding(c->argv[3]); long toremove; long removed = 0; listTypeEntry entry; int slotnum = keyHashSlot(c->argv[1]->ptr, sdslen(c->argv[1]->ptr)); if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != REDIS_OK)) return; subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero,slotnum); if (subject == NULL || checkType(c,subject,REDIS_LIST)) return; /* Make sure obj is raw when we're dealing with a ziplist */ if (subject->encoding == REDIS_ENCODING_ZIPLIST) obj = getDecodedObject(obj); listTypeIterator *li; if (toremove < 0) { toremove = -toremove; li = listTypeInitIterator(subject,-1,REDIS_HEAD); } else { li = listTypeInitIterator(subject,0,REDIS_TAIL); } while (listTypeNext(li,&entry)) { if (listTypeEqual(&entry,obj)) { listTypeDelete(&entry); server.dirty++; removed++; if (toremove && removed == toremove) break; } } listTypeReleaseIterator(li); /* Clean up raw encoded object */ if (subject->encoding == REDIS_ENCODING_ZIPLIST) decrRefCount(obj); if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1],slotnum); addReplyLongLong(c,removed); if (removed) signalModifiedKey(c->db,c->argv[1],slotnum); }
void lremCommand(client *c) { robj *subject, *obj; obj = c->argv[3]; long toremove; long removed = 0; if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != C_OK)) return; subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero); if (subject == NULL || checkType(c,subject,OBJ_LIST)) return; listTypeIterator *li; if (toremove < 0) { toremove = -toremove; li = listTypeInitIterator(subject,-1,LIST_HEAD); } else { li = listTypeInitIterator(subject,0,LIST_TAIL); } listTypeEntry entry; while (listTypeNext(li,&entry)) { if (listTypeEqual(&entry,obj)) { listTypeDelete(li, &entry); server.dirty++; removed++; if (toremove && removed == toremove) break; } } listTypeReleaseIterator(li); if (removed) { signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_GENERIC,"lrem",c->argv[1],c->db->id); } if (listTypeLength(subject) == 0) { dbDelete(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id); } addReplyLongLong(c,removed); }
/* LCOUNT KEY VALUE count the number of elements in list with value VALUE */ void lcount(client* c){ robj *subject; listTypeIterator *iter; listTypeEntry entry; int count = 0; robj *val = c->argv[2]; if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,subject,OBJ_LIST)) return; iter = listTypeInitIterator(subject,0,LIST_TAIL); while (listTypeNext(iter,&entry)) { if (listTypeEqual(&entry, val)) { count++; } } addReplyLongLong(c, count); }
void pushxGenericCommand(client *c, robj *refval, robj *val, int where) { robj *subject; listTypeIterator *iter; listTypeEntry entry; int inserted = 0; if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,subject,OBJ_LIST)) return; if (refval != NULL) { /* Seek refval from head to tail */ iter = listTypeInitIterator(subject,0,LIST_TAIL); while (listTypeNext(iter,&entry)) { if (listTypeEqual(&entry,refval)) { listTypeInsert(&entry,val,where); inserted = 1; break; } } listTypeReleaseIterator(iter); if (inserted) { signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_LIST,"linsert", c->argv[1],c->db->id); server.dirty++; } else { /* Notify client of a failed insert */ addReply(c,shared.cnegone); return; } } else { char *event = (where == LIST_HEAD) ? "lpush" : "rpush"; listTypePush(subject,val,where); signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id); server.dirty++; } addReplyLongLong(c,listTypeLength(subject)); }
/* LFIND command: find the first index after STARTPOS with value VALUE LFIND KEY STARTPOS VALUE if searching for the first occurrence, pass STARTPOS as -1 returns -1 if: 1. value not found after STARTPOS 2. or if the KEY doesn't exist in db, or type is not LIST 3. or STARTPOS is larger than LENGTH of list else return the index for value (starting from zero) Time: O(N) */ void lfindCommand(client* c) { robj *subject, *val; val = c->argv[3]; long startpos; long index; long len; bool found = false; if ((getLongFromObjectOrReply(c, c->argv[2], &startpos, NULL) != C_OK)) return; subject = lookupKeyWrite(c->db, c->argv[1]); if (subject == NULL || checkType(c,subject,OBJ_LIST) || startpos >= listTypeLength(subject)) { addReplyLongLong(c, -1); return; } listTypeIterator *li; li = listTypeInitIterator(subject, ++index, LIST_HEAD); if(li->iter == NULL) { // already moves after the last item addReplyLongLong(c, -1); return; } listTypeEntry entry; while (listTypeNext(li,&entry)) { if (listTypeEqual(&entry,val)) { found = true; break; } index++; } if(!found) { addReplyLongLong(c, -1); return; } addReplyLongLong(c, index); }
void lremCommand(redisClient *c) { robj *subject, *obj = c->argv[3]; int toremove = atoi(c->argv[2]->ptr); int removed = 0; listTypeEntry entry; subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero); if (subject == NULL || checkType(c,subject,REDIS_LIST)) return; /* Make sure obj is raw when we're dealing with a ziplist */ if (subject->encoding == REDIS_ENCODING_ZIPLIST) obj = getDecodedObject(obj); listTypeIterator *li; if (toremove < 0) { toremove = -toremove; li = listTypeInitIterator(subject,-1,REDIS_HEAD); } else { li = listTypeInitIterator(subject,0,REDIS_TAIL); } while (listTypeNext(li,&entry)) { if (listTypeEqual(&entry,obj)) { listTypeDelete(&entry); server.dirty++; removed++; if (toremove && removed == toremove) break; } } listTypeReleaseIterator(li); /* Clean up raw encoded object */ if (subject->encoding == REDIS_ENCODING_ZIPLIST) decrRefCount(obj); if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1]); addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",removed)); if (removed) touchWatchedKey(c->db,c->argv[1]); }
// LREM key count value // LREM命令 void lremCommand(client *c) { robj *subject, *obj; obj = c->argv[3]; long toremove; long removed = 0; //将字符串类型的count参数转换为long类型的整数,保存在toremove中 if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != C_OK)) return; //以写操作读取出key对象的value值 subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero); //如果key不存在或value对象不是列表类型则直接返回 if (subject == NULL || checkType(c,subject,OBJ_LIST)) return; listTypeIterator *li; if (toremove < 0) { //如果toremove小于零,则从尾部向头部删除 toremove = -toremove; //创建迭代器,指向尾部元素 li = listTypeInitIterator(subject,-1,LIST_HEAD); } else { //如果toremove大于等于零,则从头部向尾部删除,创建迭代器 li = listTypeInitIterator(subject,0,LIST_TAIL); } listTypeEntry entry; //遍历列表,保存迭代器当前指向的entry while (listTypeNext(li,&entry)) { //如果当前entry的值是obj if (listTypeEqual(&entry,obj)) { //删除当前的entry listTypeDelete(li, &entry); //更新脏键 server.dirty++; //更新计数器 removed++; //如果删除了count个,则跳出循环 if (toremove && removed == toremove) break; } } //释放迭代器 listTypeReleaseIterator(li); //如果删除成功 if (removed) { //当数据库的键被改动,则会调用该函数发送信号 signalModifiedKey(c->db,c->argv[1]); //发送"lrem"时间通知 notifyKeyspaceEvent(NOTIFY_GENERIC,"lrem",c->argv[1],c->db->id); } //如果将列表中的元素全部删除完了 if (listTypeLength(subject) == 0) { //从数据库中删除键key dbDelete(c->db,c->argv[1]); //发送"del"时间通知 notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id); } //发送删除元素的个数给client addReplyLongLong(c,removed); }