diff --git a/src/server.c b/src/server.c index cbe37a2ce..72a237214 100644 --- a/src/server.c +++ b/src/server.c @@ -145,8 +145,8 @@ struct redisCommand redisCommandTable[] = { {"mget",mgetCommand,-2,"r",0,NULL,1,-1,1,0,0}, {"rpush",rpushCommand,-3,"wmF",0,NULL,1,1,1,0,0}, {"lpush",lpushCommand,-3,"wmF",0,NULL,1,1,1,0,0}, - {"rpushx",rpushxCommand,3,"wmF",0,NULL,1,1,1,0,0}, - {"lpushx",lpushxCommand,3,"wmF",0,NULL,1,1,1,0,0}, + {"rpushx",rpushxCommand,-3,"wmF",0,NULL,1,1,1,0,0}, + {"lpushx",lpushxCommand,-3,"wmF",0,NULL,1,1,1,0,0}, {"linsert",linsertCommand,5,"wm",0,NULL,1,1,1,0,0}, {"rpop",rpopCommand,2,"wF",0,NULL,1,1,1,0,0}, {"lpop",lpopCommand,2,"wF",0,NULL,1,1,1,0,0}, diff --git a/src/t_list.c b/src/t_list.c index 7d5be11af..109aba9d0 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -195,7 +195,7 @@ void listTypeConvert(robj *subject, int enc) { *----------------------------------------------------------------------------*/ void pushGenericCommand(client *c, int where) { - int j, waiting = 0, pushed = 0; + int j, pushed = 0; robj *lobj = lookupKeyWrite(c->db,c->argv[1]); if (lobj && lobj->type != OBJ_LIST) { @@ -214,7 +214,7 @@ void pushGenericCommand(client *c, int where) { listTypePush(lobj,c->argv[j],where); pushed++; } - addReplyLongLong(c, waiting + (lobj ? listTypeLength(lobj) : 0)); + addReplyLongLong(c, (lobj ? listTypeLength(lobj) : 0)); if (pushed) { char *event = (where == LIST_HEAD) ? "lpush" : "rpush"; @@ -232,70 +232,82 @@ void rpushCommand(client *c) { pushGenericCommand(c,LIST_TAIL); } -void pushxGenericCommand(client *c, robj *refval, robj *val, int where) { +void pushxGenericCommand(client *c, int where) { + int j, pushed = 0; + robj *subject; + + if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || + checkType(c,subject,OBJ_LIST)) return; + + for (j = 2; j < c->argc; j++) { + c->argv[j] = tryObjectEncoding(c->argv[j]); + listTypePush(subject,c->argv[j],where); + pushed++; + } + + addReplyLongLong(c,listTypeLength(subject)); + + if (pushed) { + char *event = (where == LIST_HEAD) ? "lpush" : "rpush"; + signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id); + } + server.dirty += pushed; +} + +void lpushxCommand(client *c) { + pushxGenericCommand(c,LIST_HEAD); +} + +void rpushxCommand(client *c) { + pushxGenericCommand(c,LIST_TAIL); +} + +void linsertCommand(client *c) { + int where; robj *subject; listTypeIterator *iter; listTypeEntry entry; int inserted = 0; + c->argv[4] = tryObjectEncoding(c->argv[4]); + if (strcasecmp(c->argv[2]->ptr,"after") == 0) { + where = LIST_TAIL; + } else if (strcasecmp(c->argv[2]->ptr,"before") == 0) { + where = LIST_HEAD; + } else { + addReply(c,shared.syntaxerr); + return; + } + 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; - } + /* Seek pivot from head to tail */ + iter = listTypeInitIterator(subject,0,LIST_TAIL); + while (listTypeNext(iter,&entry)) { + if (listTypeEqual(&entry,c->argv[3])) { + listTypeInsert(&entry,c->argv[4],where); + inserted = 1; + break; } - listTypeReleaseIterator(iter); + } + 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); + if (inserted) { signalModifiedKey(c->db,c->argv[1]); - notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id); + notifyKeyspaceEvent(NOTIFY_LIST,"linsert", + c->argv[1],c->db->id); server.dirty++; + } else { + /* Notify client of a failed insert */ + addReply(c,shared.cnegone); + return; } addReplyLongLong(c,listTypeLength(subject)); } -void lpushxCommand(client *c) { - c->argv[2] = tryObjectEncoding(c->argv[2]); - pushxGenericCommand(c,NULL,c->argv[2],LIST_HEAD); -} - -void rpushxCommand(client *c) { - c->argv[2] = tryObjectEncoding(c->argv[2]); - pushxGenericCommand(c,NULL,c->argv[2],LIST_TAIL); -} - -void linsertCommand(client *c) { - c->argv[4] = tryObjectEncoding(c->argv[4]); - if (strcasecmp(c->argv[2]->ptr,"after") == 0) { - pushxGenericCommand(c,c->argv[3],c->argv[4],LIST_TAIL); - } else if (strcasecmp(c->argv[2]->ptr,"before") == 0) { - pushxGenericCommand(c,c->argv[3],c->argv[4],LIST_HEAD); - } else { - addReply(c,shared.syntaxerr); - } -} - void llenCommand(client *c) { robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero); if (o == NULL || checkType(c,o,OBJ_LIST)) return; diff --git a/tests/unit/type/list.tcl b/tests/unit/type/list.tcl index e4d568cf1..1557082a2 100644 --- a/tests/unit/type/list.tcl +++ b/tests/unit/type/list.tcl @@ -507,7 +507,9 @@ start_server { create_list xlist "$large c" assert_equal 3 [r rpushx xlist d] assert_equal 4 [r lpushx xlist a] - assert_equal "a $large c d" [r lrange xlist 0 -1] + assert_equal 6 [r rpushx xlist 42 x] + assert_equal 9 [r lpushx xlist y3 y2 y1] + assert_equal "y1 y2 y3 a $large c d 42 x" [r lrange xlist 0 -1] } test "LINSERT - $type" {