X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/d433ebc6810b15c21120e502dea3a27fc2a5b348..3bcffcbe5bc0acde311d0fb311f893400cd52e0e:/src/t_zset.c diff --git a/src/t_zset.c b/src/t_zset.c index 5df8f288..27522367 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -174,25 +174,35 @@ int zslDelete(zskiplist *zsl, double score, robj *obj) { return 0; /* not found */ } +/* Struct to hold a inclusive/exclusive range spec. */ +typedef struct { + double min, max; + int minex, maxex; /* are min or max exclusive? */ +} zrangespec; + /* Delete all the elements with score between min and max from the skiplist. * Min and mx are inclusive, so a score >= min || score <= max is deleted. * Note that this function takes the reference to the hash table view of the * sorted set, in order to remove the elements from the hash table too. */ -unsigned long zslDeleteRangeByScore(zskiplist *zsl, double min, double max, dict *dict) { +unsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec range, dict *dict) { zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; unsigned long removed = 0; int i; x = zsl->header; for (i = zsl->level-1; i >= 0; i--) { - while (x->level[i].forward && x->level[i].forward->score < min) - x = x->level[i].forward; + while (x->level[i].forward && (range.minex ? + x->level[i].forward->score <= range.min : + x->level[i].forward->score < range.min)) + x = x->level[i].forward; update[i] = x; } - /* We may have multiple elements with the same score, what we need - * is to find the element with both the right score and object. */ + + /* Current node is the last with score < or <= min. */ x = x->level[0].forward; - while (x && x->score <= max) { + + /* Delete nodes while in range. */ + while (x && (range.maxex ? x->score < range.max : x->score <= range.max)) { zskiplistNode *next = x->level[0].forward; zslDeleteNode(zsl,x,update); dictDelete(dict,x->obj); @@ -200,7 +210,7 @@ unsigned long zslDeleteRangeByScore(zskiplist *zsl, double min, double max, dict removed++; x = next; } - return removed; /* not found */ + return removed; } /* Delete all the elements with rank between start and end from the skiplist. @@ -296,13 +306,9 @@ zskiplistNode* zslistTypeGetElementByRank(zskiplist *zsl, unsigned long rank) { return NULL; } -typedef struct { - double min, max; - int minex, maxex; /* are min or max exclusive? */ -} zrangespec; - /* Populate the rangespec according to the objects min and max. */ -int zslParseRange(robj *min, robj *max, zrangespec *spec) { +static int zslParseRange(robj *min, robj *max, zrangespec *spec) { + char *eptr; spec->minex = spec->maxex = 0; /* Parse the min-max interval. If one of the values is prefixed @@ -313,20 +319,24 @@ int zslParseRange(robj *min, robj *max, zrangespec *spec) { spec->min = (long)min->ptr; } else { if (((char*)min->ptr)[0] == '(') { - spec->min = strtod((char*)min->ptr+1,NULL); + spec->min = strtod((char*)min->ptr+1,&eptr); + if (eptr[0] != '\0' || isnan(spec->min)) return REDIS_ERR; spec->minex = 1; } else { - spec->min = strtod((char*)min->ptr,NULL); + spec->min = strtod((char*)min->ptr,&eptr); + if (eptr[0] != '\0' || isnan(spec->min)) return REDIS_ERR; } } if (max->encoding == REDIS_ENCODING_INT) { spec->max = (long)max->ptr; } else { if (((char*)max->ptr)[0] == '(') { - spec->max = strtod((char*)max->ptr+1,NULL); + spec->max = strtod((char*)max->ptr+1,&eptr); + if (eptr[0] != '\0' || isnan(spec->max)) return REDIS_ERR; spec->maxex = 1; } else { - spec->max = strtod((char*)max->ptr,NULL); + spec->max = strtod((char*)max->ptr,&eptr); + if (eptr[0] != '\0' || isnan(spec->max)) return REDIS_ERR; } } @@ -389,7 +399,7 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double score, int de = dictFind(zs->dict,ele); redisAssert(de != NULL); dictGetEntryVal(de) = &znode->score; - touchWatchedKey(c->db,c->argv[1]); + signalModifiedKey(c->db,c->argv[1]); server.dirty++; if (incr) addReplyDouble(c,score); @@ -417,7 +427,7 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double score, int /* Update the score in the current dict entry */ dictGetEntryVal(de) = &znode->score; - touchWatchedKey(c->db,c->argv[1]); + signalModifiedKey(c->db,c->argv[1]); server.dirty++; } if (incr) @@ -430,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); } @@ -450,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); @@ -464,29 +477,31 @@ void zremCommand(redisClient *c) { dictDelete(zs->dict,c->argv[2]); if (htNeedsResize(zs->dict)) dictResize(zs->dict); if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]); - touchWatchedKey(c->db,c->argv[1]); + signalModifiedKey(c->db,c->argv[1]); server.dirty++; addReply(c,shared.cone); } void zremrangebyscoreCommand(redisClient *c) { - double min; - double max; + zrangespec range; long deleted; - robj *zsetobj; + robj *o; zset *zs; - if ((getDoubleFromObjectOrReply(c, c->argv[2], &min, NULL) != REDIS_OK) || - (getDoubleFromObjectOrReply(c, c->argv[3], &max, NULL) != REDIS_OK)) return; + /* Parse the range arguments. */ + if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) { + addReplyError(c,"min or max is not a double"); + return; + } - if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || - checkType(c,zsetobj,REDIS_ZSET)) return; + if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || + checkType(c,o,REDIS_ZSET)) return; - zs = zsetobj->ptr; - deleted = zslDeleteRangeByScore(zs->zsl,min,max,zs->dict); + zs = o->ptr; + deleted = zslDeleteRangeByScore(zs->zsl,range,zs->dict); if (htNeedsResize(zs->dict)) dictResize(zs->dict); if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]); - if (deleted) touchWatchedKey(c->db,c->argv[1]); + if (deleted) signalModifiedKey(c->db,c->argv[1]); server.dirty += deleted; addReplyLongLong(c,deleted); } @@ -525,7 +540,7 @@ void zremrangebyrankCommand(redisClient *c) { deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict); if (htNeedsResize(zs->dict)) dictResize(zs->dict); if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]); - if (deleted) touchWatchedKey(c->db,c->argv[1]); + if (deleted) signalModifiedKey(c->db,c->argv[1]); server.dirty += deleted; addReplyLongLong(c, deleted); } @@ -664,8 +679,8 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) { di = dictGetIterator(src[0].dict); while((de = dictNext(di)) != NULL) { double score, value; - score = src[0].weight * zunionInterDictValue(de); + score = src[0].weight * zunionInterDictValue(de); for (j = 1; j < setnum; j++) { dictEntry *other = dictFind(src[j].dict,dictGetEntryKey(de)); if (other) { @@ -726,14 +741,14 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) { } if (dbDelete(c->db,dstkey)) { - touchWatchedKey(c->db,dstkey); + signalModifiedKey(c->db,dstkey); touched = 1; server.dirty++; } if (dstzset->zsl->length) { dbAdd(c->db,dstkey,dstobj); addReplyLongLong(c, dstzset->zsl->length); - if (!touched) touchWatchedKey(c->db,dstkey); + if (!touched) signalModifiedKey(c->db,dstkey); server.dirty++; } else { decrRefCount(dstobj); @@ -834,7 +849,10 @@ void genericZrangebyscoreCommand(redisClient *c, int reverse, int justcount) { void *replylen = NULL; /* Parse the range arguments. */ - zslParseRange(c->argv[2],c->argv[3],&range); + if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) { + addReplyError(c,"min or max is not a double"); + return; + } /* Parse optional extra arguments. Note that ZCOUNT will exactly have * 4 arguments, so we'll never enter the following code path. */ @@ -989,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); @@ -1012,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);