]> git.saurik.com Git - redis.git/commitdiff
Merge branch 'master' into zrevrangebyscore
authorPieter Noordhuis <pcnoordhuis@gmail.com>
Wed, 13 Oct 2010 18:29:50 +0000 (20:29 +0200)
committerPieter Noordhuis <pcnoordhuis@gmail.com>
Wed, 13 Oct 2010 18:29:50 +0000 (20:29 +0200)
1  2 
src/redis.c
src/redis.h
src/t_zset.c

diff --combined src/redis.c
index fca5f1b3e2e554f89d422913f72d34954a905eb5,27a855d97a0d0f6d4ff5c7f5328af250f6bc7c91..8f208926b82d48726afabba40d5fc7cf878ae058
@@@ -120,7 -120,6 +120,7 @@@ struct redisCommand readonlyCommandTabl
      {"zinterstore",zinterstoreCommand,-4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0},
      {"zrange",zrangeCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
      {"zrangebyscore",zrangebyscoreCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
 +    {"zrevrangebyscore",zrevrangebyscoreCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
      {"zcount",zcountCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
      {"zrevrange",zrevrangeCommand,-4,REDIS_CMD_INLINE,NULL,1,1,1},
      {"zcard",zcardCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
@@@ -890,9 -889,6 +890,6 @@@ void call(redisClient *c, struct redisC
  int processCommand(redisClient *c) {
      struct redisCommand *cmd;
  
-     /* Free some memory if needed (maxmemory setting) */
-     if (server.maxmemory) freeMemoryIfNeeded();
      /* Handle the multi bulk command type. This is an alternative protocol
       * supported by Redis in order to receive commands that are composed of
       * multiple binary-safe "bulk" arguments. The latency of processing is
          return 1;
      }
  
-     /* Handle the maxmemory directive */
+     /* Handle the maxmemory directive.
+      *
+      * First we try to free some memory if possible (if there are volatile
+      * keys in the dataset). If there are not the only thing we can do
+      * is returning an error. */
+     if (server.maxmemory) freeMemoryIfNeeded();
      if (server.maxmemory && (cmd->flags & REDIS_CMD_DENYOOM) &&
          zmalloc_used_memory() > server.maxmemory)
      {
diff --combined src/redis.h
index d53ccf5ba836362afde41387534f943881d94daa,3e9fc2369cdfacf9f3f3536be582b22c34791fba..24dfb9b5ea44680f035e1b43f1a6fac5e4a61174
@@@ -286,6 -286,7 +286,7 @@@ typedef struct redisClient 
      int dictid;
      sds querybuf;
      robj **argv, **mbargv;
+     char *newline;          /* pointing to the detected newline in querybuf */
      int argc, mbargc;
      long bulklen;            /* bulk read len. -1 if not in bulk read mode */
      int multibulk;          /* multi bulk command format active */
@@@ -895,7 -896,6 +896,7 @@@ void zaddCommand(redisClient *c)
  void zincrbyCommand(redisClient *c);
  void zrangeCommand(redisClient *c);
  void zrangebyscoreCommand(redisClient *c);
 +void zrevrangebyscoreCommand(redisClient *c);
  void zcountCommand(redisClient *c);
  void zrevrangeCommand(redisClient *c);
  void zcardCommand(redisClient *c);
diff --combined src/t_zset.c
index 5df8f28833f8972cbaf5d6491599132d00c217c8,114c95d627b041b264add3ea0281694095a949c3..1a67febcc4846bcb84f164483dc1c69d523fab8b
@@@ -296,44 -296,6 +296,44 @@@ zskiplistNode* zslistTypeGetElementByRa
      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) {
 +    spec->minex = spec->maxex = 0;
 +
 +    /* Parse the min-max interval. If one of the values is prefixed
 +     * by the "(" character, it's considered "open". For instance
 +     * ZRANGEBYSCORE zset (1.5 (2.5 will match min < x < max
 +     * ZRANGEBYSCORE zset 1.5 2.5 will instead match min <= x <= max */
 +    if (min->encoding == REDIS_ENCODING_INT) {
 +        spec->min = (long)min->ptr;
 +    } else {
 +        if (((char*)min->ptr)[0] == '(') {
 +            spec->min = strtod((char*)min->ptr+1,NULL);
 +            spec->minex = 1;
 +        } else {
 +            spec->min = strtod((char*)min->ptr,NULL);
 +        }
 +    }
 +    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->maxex = 1;
 +        } else {
 +            spec->max = strtod((char*)max->ptr,NULL);
 +        }
 +    }
 +
 +    return REDIS_OK;
 +}
 +
 +
  /*-----------------------------------------------------------------------------
   * Sorted set commands 
   *----------------------------------------------------------------------------*/
@@@ -664,19 -626,19 +664,19 @@@ void zunionInterGenericCommand(redisCli
              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) {
                          value = src[j].weight * zunionInterDictValue(other);
 -                        zunionInterAggregate(&score, value, aggregate);
 +                        zunionInterAggregate(&score,value,aggregate);
                      } else {
                          break;
                      }
                  }
  
 -                /* accept entry only when present in every source dict */
 +                /* Only continue when present in every source dict. */
                  if (j == setnum) {
                      robj *o = dictGetEntryKey(de);
                      znode = zslInsert(dstzset->zsl,score,o);
                  /* skip key when already processed */
                  if (dictFind(dstzset->dict,dictGetEntryKey(de)) != NULL)
                      continue;
 +
 +                /* initialize score */
                  score = src[i].weight * zunionInterDictValue(de);
  
                  /* because the zsets are sorted by size, its only possible
                      dictEntry *other = dictFind(src[j].dict,dictGetEntryKey(de));
                      if (other) {
                          value = src[j].weight * zunionInterDictValue(other);
 -                        zunionInterAggregate(&score, value, aggregate);
 +                        zunionInterAggregate(&score,value,aggregate);
                      }
                  }
  
@@@ -820,153 -780,125 +820,153 @@@ void zrevrangeCommand(redisClient *c) 
      zrangeGenericCommand(c,1);
  }
  
 -/* This command implements both ZRANGEBYSCORE and ZCOUNT.
 - * If justcount is non-zero, just the count is returned. */
 -void genericZrangebyscoreCommand(redisClient *c, int justcount) {
 -    robj *o;
 -    double min, max;
 -    int minex = 0, maxex = 0; /* are min or max exclusive? */
 +/* This command implements ZRANGEBYSCORE, ZREVRANGEBYSCORE and ZCOUNT.
 + * If "justcount", only the number of elements in the range is returned. */
 +void genericZrangebyscoreCommand(redisClient *c, int reverse, int justcount) {
 +    zrangespec range;
 +    robj *o, *emptyreply;
 +    zset *zsetobj;
 +    zskiplist *zsl;
 +    zskiplistNode *ln;
      int offset = 0, limit = -1;
      int withscores = 0;
 -    int badsyntax = 0;
 +    unsigned long rangelen = 0;
 +    void *replylen = NULL;
  
 -    /* Parse the min-max interval. If one of the values is prefixed
 -     * by the "(" character, it's considered "open". For instance
 -     * ZRANGEBYSCORE zset (1.5 (2.5 will match min < x < max
 -     * ZRANGEBYSCORE zset 1.5 2.5 will instead match min <= x <= max */
 -    if (((char*)c->argv[2]->ptr)[0] == '(') {
 -        min = strtod((char*)c->argv[2]->ptr+1,NULL);
 -        minex = 1;
 -    } else {
 -        min = strtod(c->argv[2]->ptr,NULL);
 +    /* Parse the range arguments. */
 +    zslParseRange(c->argv[2],c->argv[3],&range);
 +
 +    /* Parse optional extra arguments. Note that ZCOUNT will exactly have
 +     * 4 arguments, so we'll never enter the following code path. */
 +    if (c->argc > 4) {
 +        int remaining = c->argc - 4;
 +        int pos = 4;
 +
 +        while (remaining) {
 +            if (remaining >= 1 && !strcasecmp(c->argv[pos]->ptr,"withscores")) {
 +                pos++; remaining--;
 +                withscores = 1;
 +            } else if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,"limit")) {
 +                offset = atoi(c->argv[pos+1]->ptr);
 +                limit = atoi(c->argv[pos+2]->ptr);
 +                pos += 3; remaining -= 3;
 +            } else {
 +                addReply(c,shared.syntaxerr);
 +                return;
 +            }
 +        }
      }
 -    if (((char*)c->argv[3]->ptr)[0] == '(') {
 -        max = strtod((char*)c->argv[3]->ptr+1,NULL);
 -        maxex = 1;
 +
 +    /* Ok, lookup the key and get the range */
 +    emptyreply = justcount ? shared.czero : shared.emptymultibulk;
 +    if ((o = lookupKeyReadOrReply(c,c->argv[1],emptyreply)) == NULL ||
 +        checkType(c,o,REDIS_ZSET)) return;
 +    zsetobj = o->ptr;
 +    zsl = zsetobj->zsl;
 +
 +    /* If reversed, assume the elements are sorted from high to low score. */
 +    ln = zslFirstWithScore(zsl,range.min);
 +    if (reverse) {
 +        /* If range.min is out of range, ln will be NULL and we need to use
 +         * the tail of the skiplist as first node of the range. */
 +        if (ln == NULL) ln = zsl->tail;
 +
 +        /* zslFirstWithScore returns the first element with where with
 +         * score >= range.min, so backtrack to make sure the element we use
 +         * here has score <= range.min. */
 +        while (ln && ln->score > range.min) ln = ln->backward;
 +
 +        /* Move to the right element according to the range spec. */
 +        if (range.minex) {
 +            /* Find last element with score < range.min */
 +            while (ln && ln->score == range.min) ln = ln->backward;
 +        } else {
 +            /* Find last element with score <= range.min */
 +            while (ln && ln->level[0].forward &&
 +                         ln->level[0].forward->score == range.min)
 +                ln = ln->level[0].forward;
 +        }
      } else {
 -        max = strtod(c->argv[3]->ptr,NULL);
 +        if (range.minex) {
 +            /* Find first element with score > range.min */
 +            while (ln && ln->score == range.min) ln = ln->level[0].forward;
 +        }
      }
  
 -    /* Parse "WITHSCORES": note that if the command was called with
 -     * the name ZCOUNT then we are sure that c->argc == 4, so we'll never
 -     * enter the following paths to parse WITHSCORES and LIMIT. */
 -    if (c->argc == 5 || c->argc == 8) {
 -        if (strcasecmp(c->argv[c->argc-1]->ptr,"withscores") == 0)
 -            withscores = 1;
 -        else
 -            badsyntax = 1;
 -    }
 -    if (c->argc != (4 + withscores) && c->argc != (7 + withscores))
 -        badsyntax = 1;
 -    if (badsyntax) {
 -        addReplyError(c,"wrong number of arguments for ZRANGEBYSCORE");
 +    /* No "first" element in the specified interval. */
 +    if (ln == NULL) {
 +        addReply(c,emptyreply);
          return;
      }
  
 -    /* Parse "LIMIT" */
 -    if (c->argc == (7 + withscores) && strcasecmp(c->argv[4]->ptr,"limit")) {
 -        addReply(c,shared.syntaxerr);
 -        return;
 -    } else if (c->argc == (7 + withscores)) {
 -        offset = atoi(c->argv[5]->ptr);
 -        limit = atoi(c->argv[6]->ptr);
 -        if (offset < 0) offset = 0;
 -    }
 +    /* We don't know in advance how many matching elements there
 +     * are in the list, so we push this object that will represent
 +     * the multi-bulk length in the output buffer, and will "fix"
 +     * it later */
 +    if (!justcount)
 +        replylen = addDeferredMultiBulkLength(c);
  
 -    /* Ok, lookup the key and get the range */
 -    o = lookupKeyRead(c->db,c->argv[1]);
 -    if (o == NULL) {
 -        addReply(c,justcount ? shared.czero : shared.emptymultibulk);
 -    } else {
 -        if (o->type != REDIS_ZSET) {
 -            addReply(c,shared.wrongtypeerr);
 -        } else {
 -            zset *zsetobj = o->ptr;
 -            zskiplist *zsl = zsetobj->zsl;
 -            zskiplistNode *ln;
 -            robj *ele;
 -            void *replylen = NULL;
 -            unsigned long rangelen = 0;
 -
 -            /* Get the first node with the score >= min, or with
 -             * score > min if 'minex' is true. */
 -            ln = zslFirstWithScore(zsl,min);
 -            while (minex && ln && ln->score == min) ln = ln->level[0].forward;
 -
 -            if (ln == NULL) {
 -                /* No element matching the speciifed interval */
 -                addReply(c,justcount ? shared.czero : shared.emptymultibulk);
 -                return;
 -            }
 +    /* If there is an offset, just traverse the number of elements without
 +     * checking the score because that is done in the next loop. */
 +    while(ln && offset--) {
 +        if (reverse)
 +            ln = ln->backward;
 +        else
 +            ln = ln->level[0].forward;
 +    }
  
 -            /* We don't know in advance how many matching elements there
 -             * are in the list, so we push this object that will represent
 -             * the multi-bulk length in the output buffer, and will "fix"
 -             * it later */
 -            if (!justcount)
 -                replylen = addDeferredMultiBulkLength(c);
 -
 -            while(ln && (maxex ? (ln->score < max) : (ln->score <= max))) {
 -                if (offset) {
 -                    offset--;
 -                    ln = ln->level[0].forward;
 -                    continue;
 -                }
 -                if (limit == 0) break;
 -                if (!justcount) {
 -                    ele = ln->obj;
 -                    addReplyBulk(c,ele);
 -                    if (withscores)
 -                        addReplyDouble(c,ln->score);
 -                }
 -                ln = ln->level[0].forward;
 -                rangelen++;
 -                if (limit > 0) limit--;
 +    while (ln && limit--) {
 +        /* Check if this this element is in range. */
 +        if (reverse) {
 +            if (range.maxex) {
 +                /* Element should have score > range.max */
 +                if (ln->score <= range.max) break;
 +            } else {
 +                /* Element should have score >= range.max */
 +                if (ln->score < range.max) break;
              }
 -            if (justcount) {
 -                addReplyLongLong(c,(long)rangelen);
 +        } else {
 +            if (range.maxex) {
 +                /* Element should have score < range.max */
 +                if (ln->score >= range.max) break;
              } else {
 -                setDeferredMultiBulkLength(c,replylen,
 -                     withscores ? (rangelen*2) : rangelen);
 +                /* Element should have score <= range.max */
 +                if (ln->score > range.max) break;
              }
          }
 +
 +        /* Do our magic */
 +        rangelen++;
 +        if (!justcount) {
 +            addReplyBulk(c,ln->obj);
 +            if (withscores)
 +                addReplyDouble(c,ln->score);
 +        }
 +
 +        if (reverse)
 +            ln = ln->backward;
 +        else
 +            ln = ln->level[0].forward;
 +    }
 +
 +    if (justcount) {
 +        addReplyLongLong(c,(long)rangelen);
 +    } else {
 +        setDeferredMultiBulkLength(c,replylen,
 +             withscores ? (rangelen*2) : rangelen);
      }
  }
  
  void zrangebyscoreCommand(redisClient *c) {
 -    genericZrangebyscoreCommand(c,0);
 +    genericZrangebyscoreCommand(c,0,0);
 +}
 +
 +void zrevrangebyscoreCommand(redisClient *c) {
 +    genericZrangebyscoreCommand(c,1,0);
  }
  
  void zcountCommand(redisClient *c) {
 -    genericZrangebyscoreCommand(c,1);
 +    genericZrangebyscoreCommand(c,0,1);
  }
  
  void zcardCommand(redisClient *c) {