diff options
author | antirez <antirez@gmail.com> | 2012-02-01 15:22:28 +0100 |
---|---|---|
committer | antirez <antirez@gmail.com> | 2012-02-01 15:22:28 +0100 |
commit | 2c861050c17237a61fdaff4da2777c5d18ce979a (patch) | |
tree | 09a3f9e9f9525c1e3e1543bcd641ced6e090c47c /src/sort.c | |
parent | 548efd91e5a33e1358213a902b8533d88f40f7cf (diff) | |
download | redis-2c861050c17237a61fdaff4da2777c5d18ce979a.tar.gz |
SORT is now more deterministic: does not accept to compare by score items that have scores not representing a valid double. Also items with the same score are compared lexycographically. At the same time the scripting side introduced the ability to sort the output of SORT when sort uses the BY <constant> optimization, resulting in no specific ordering. Since in this case the user may use GET, and the result of GET can be null, converted into false as Lua data type, this commit also introduces the ability to sort Lua tables containining false, only if the first (faster) attempt at using just table.sort with a single argument fails.
Diffstat (limited to 'src/sort.c')
-rw-r--r-- | src/sort.c | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/src/sort.c b/src/sort.c index 038aec27a..11b73ad38 100644 --- a/src/sort.c +++ b/src/sort.c @@ -1,5 +1,6 @@ #include "redis.h" #include "pqsort.h" /* Partial qsort for SORT+LIMIT */ +#include <math.h> /* isnan() */ redisSortOperation *createSortOperation(int type, robj *pattern) { redisSortOperation *so = zmalloc(sizeof(*so)); @@ -102,7 +103,10 @@ int sortCompare(const void *s1, const void *s2) { } else if (so1->u.score < so2->u.score) { cmp = -1; } else { - cmp = 0; + /* Objects have the same score, but we don't want the comparison + * to be undefined, so we compare objects lexicographycally. + * This way the result of SORT is deterministic. */ + cmp = compareStringObjects(so1->obj,so2->obj); } } else { /* Alphanumeric sorting */ @@ -136,6 +140,7 @@ void sortCommand(redisClient *c) { long limit_start = 0, limit_count = -1, start, end; int j, dontsort = 0, vectorlen; int getop = 0; /* GET operation counter */ + int int_convertion_error = 0; robj *sortval, *sortby = NULL, *storekey = NULL; redisSortObject *vector; /* Resulting vector to sort */ @@ -266,7 +271,14 @@ void sortCommand(redisClient *c) { if (sortby) vector[j].u.cmpobj = getDecodedObject(byval); } else { if (byval->encoding == REDIS_ENCODING_RAW) { - vector[j].u.score = strtod(byval->ptr,NULL); + char *eptr; + + vector[j].u.score = strtod(byval->ptr,&eptr); + if (eptr[0] != '\0' || errno == ERANGE || + isnan(vector[j].u.score)) + { + int_convertion_error = 1; + } } else if (byval->encoding == REDIS_ENCODING_INT) { /* Don't need to decode the object if it's * integer-encoded (the only encoding supported) so @@ -295,6 +307,7 @@ void sortCommand(redisClient *c) { } if (end >= vectorlen) end = vectorlen-1; + server.sort_dontsort = dontsort; if (dontsort == 0) { server.sort_desc = desc; server.sort_alpha = alpha; @@ -308,7 +321,9 @@ void sortCommand(redisClient *c) { /* Send command output to the output buffer, performing the specified * GET/DEL/INCR/DECR operations if any. */ outputlen = getop ? getop*(end-start+1) : end-start+1; - if (storekey == NULL) { + if (int_convertion_error) { + addReplyError(c,"One or more scores can't be converted into double"); + } else if (storekey == NULL) { /* STORE option not specified, sent the sorting result to client */ addReplyMultiBulkLen(c,outputlen); for (j = start; j <= end; j++) { |