Bug Summary

File:src/sort.c
Warning:line 320, column 14
Value stored to 'vectorlen' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name sort.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mthread-model posix -mframe-pointer=none -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/llvm-10/lib/clang/10.0.0 -D REDIS_STATIC= -I ../deps/hiredis -I ../deps/linenoise -I ../deps/lua/src -I ../deps/hdr_histogram -D USE_JEMALLOC -I ../deps/jemalloc/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-10/lib/clang/10.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-c11-extensions -Wno-missing-field-initializers -std=c11 -fdebug-compilation-dir /home/netto/Desktop/redis-6.2.1/src -ferror-limit 19 -fmessage-length 0 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fdiagnostics-show-option -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-03-14-133648-8817-1 -x c sort.c
1/* SORT command and helper functions.
2 *
3 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
32#include "server.h"
33#include "pqsort.h" /* Partial qsort for SORT+LIMIT */
34#include <math.h> /* isnan() */
35
36zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank);
37
38redisSortOperation *createSortOperation(int type, robj *pattern) {
39 redisSortOperation *so = zmalloc(sizeof(*so));
40 so->type = type;
41 so->pattern = pattern;
42 return so;
43}
44
45/* Return the value associated to the key with a name obtained using
46 * the following rules:
47 *
48 * 1) The first occurrence of '*' in 'pattern' is substituted with 'subst'.
49 *
50 * 2) If 'pattern' matches the "->" string, everything on the left of
51 * the arrow is treated as the name of a hash field, and the part on the
52 * left as the key name containing a hash. The value of the specified
53 * field is returned.
54 *
55 * 3) If 'pattern' equals "#", the function simply returns 'subst' itself so
56 * that the SORT command can be used like: SORT key GET # to retrieve
57 * the Set/List elements directly.
58 *
59 * The returned object will always have its refcount increased by 1
60 * when it is non-NULL. */
61robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst, int writeflag) {
62 char *p, *f, *k;
63 sds spat, ssub;
64 robj *keyobj, *fieldobj = NULL((void*)0), *o;
65 int prefixlen, sublen, postfixlen, fieldlen;
66
67 /* If the pattern is "#" return the substitution object itself in order
68 * to implement the "SORT ... GET #" feature. */
69 spat = pattern->ptr;
70 if (spat[0] == '#' && spat[1] == '\0') {
71 incrRefCount(subst);
72 return subst;
73 }
74
75 /* The substitution object may be specially encoded. If so we create
76 * a decoded object on the fly. Otherwise getDecodedObject will just
77 * increment the ref count, that we'll decrement later. */
78 subst = getDecodedObject(subst);
79 ssub = subst->ptr;
80
81 /* If we can't find '*' in the pattern we return NULL as to GET a
82 * fixed key does not make sense. */
83 p = strchr(spat,'*');
84 if (!p) {
85 decrRefCount(subst);
86 return NULL((void*)0);
87 }
88
89 /* Find out if we're dealing with a hash dereference. */
90 if ((f = strstr(p+1, "->")) != NULL((void*)0) && *(f+2) != '\0') {
91 fieldlen = sdslen(spat)-(f-spat)-2;
92 fieldobj = createStringObject(f+2,fieldlen);
93 } else {
94 fieldlen = 0;
95 }
96
97 /* Perform the '*' substitution. */
98 prefixlen = p-spat;
99 sublen = sdslen(ssub);
100 postfixlen = sdslen(spat)-(prefixlen+1)-(fieldlen ? fieldlen+2 : 0);
101 keyobj = createStringObject(NULL((void*)0),prefixlen+sublen+postfixlen);
102 k = keyobj->ptr;
103 memcpy(k,spat,prefixlen);
104 memcpy(k+prefixlen,ssub,sublen);
105 memcpy(k+prefixlen+sublen,p+1,postfixlen);
106 decrRefCount(subst); /* Incremented by decodeObject() */
107
108 /* Lookup substituted key */
109 if (!writeflag)
110 o = lookupKeyRead(db,keyobj);
111 else
112 o = lookupKeyWrite(db,keyobj);
113 if (o == NULL((void*)0)) goto noobj;
114
115 if (fieldobj) {
116 if (o->type != OBJ_HASH4) goto noobj;
117
118 /* Retrieve value from hash by the field name. The returned object
119 * is a new object with refcount already incremented. */
120 o = hashTypeGetValueObject(o, fieldobj->ptr);
121 } else {
122 if (o->type != OBJ_STRING0) goto noobj;
123
124 /* Every object that this function returns needs to have its refcount
125 * increased. sortCommand decreases it again. */
126 incrRefCount(o);
127 }
128 decrRefCount(keyobj);
129 if (fieldobj) decrRefCount(fieldobj);
130 return o;
131
132noobj:
133 decrRefCount(keyobj);
134 if (fieldlen) decrRefCount(fieldobj);
135 return NULL((void*)0);
136}
137
138/* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
139 * the additional parameter is not standard but a BSD-specific we have to
140 * pass sorting parameters via the global 'server' structure */
141int sortCompare(const void *s1, const void *s2) {
142 const redisSortObject *so1 = s1, *so2 = s2;
143 int cmp;
144
145 if (!server.sort_alpha) {
146 /* Numeric sorting. Here it's trivial as we precomputed scores */
147 if (so1->u.score > so2->u.score) {
148 cmp = 1;
149 } else if (so1->u.score < so2->u.score) {
150 cmp = -1;
151 } else {
152 /* Objects have the same score, but we don't want the comparison
153 * to be undefined, so we compare objects lexicographically.
154 * This way the result of SORT is deterministic. */
155 cmp = compareStringObjects(so1->obj,so2->obj);
156 }
157 } else {
158 /* Alphanumeric sorting */
159 if (server.sort_bypattern) {
160 if (!so1->u.cmpobj || !so2->u.cmpobj) {
161 /* At least one compare object is NULL */
162 if (so1->u.cmpobj == so2->u.cmpobj)
163 cmp = 0;
164 else if (so1->u.cmpobj == NULL((void*)0))
165 cmp = -1;
166 else
167 cmp = 1;
168 } else {
169 /* We have both the objects, compare them. */
170 if (server.sort_store) {
171 cmp = compareStringObjects(so1->u.cmpobj,so2->u.cmpobj);
172 } else {
173 /* Here we can use strcoll() directly as we are sure that
174 * the objects are decoded string objects. */
175 cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);
176 }
177 }
178 } else {
179 /* Compare elements directly. */
180 if (server.sort_store) {
181 cmp = compareStringObjects(so1->obj,so2->obj);
182 } else {
183 cmp = collateStringObjects(so1->obj,so2->obj);
184 }
185 }
186 }
187 return server.sort_desc ? -cmp : cmp;
188}
189
190/* The SORT command is the most complex command in Redis. Warning: this code
191 * is optimized for speed and a bit less for readability */
192void sortCommand(client *c) {
193 list *operations;
194 unsigned int outputlen = 0;
195 int desc = 0, alpha = 0;
196 long limit_start = 0, limit_count = -1, start, end;
197 int j, dontsort = 0, vectorlen;
198 int getop = 0; /* GET operation counter */
199 int int_conversion_error = 0;
200 int syntax_error = 0;
201 robj *sortval, *sortby = NULL((void*)0), *storekey = NULL((void*)0);
202 redisSortObject *vector; /* Resulting vector to sort */
203
204 /* Create a list of operations to perform for every sorted element.
205 * Operations can be GET */
206 operations = listCreate();
207 listSetFreeMethod(operations,zfree)((operations)->free = (zfree));
208 j = 2; /* options start at argv[2] */
209
210 /* The SORT command has an SQL-alike syntax, parse it */
211 while(j < c->argc) {
212 int leftargs = c->argc-j-1;
213 if (!strcasecmp(c->argv[j]->ptr,"asc")) {
214 desc = 0;
215 } else if (!strcasecmp(c->argv[j]->ptr,"desc")) {
216 desc = 1;
217 } else if (!strcasecmp(c->argv[j]->ptr,"alpha")) {
218 alpha = 1;
219 } else if (!strcasecmp(c->argv[j]->ptr,"limit") && leftargs >= 2) {
220 if ((getLongFromObjectOrReply(c, c->argv[j+1], &limit_start, NULL((void*)0))
221 != C_OK0) ||
222 (getLongFromObjectOrReply(c, c->argv[j+2], &limit_count, NULL((void*)0))
223 != C_OK0))
224 {
225 syntax_error++;
226 break;
227 }
228 j+=2;
229 } else if (!strcasecmp(c->argv[j]->ptr,"store") && leftargs >= 1) {
230 storekey = c->argv[j+1];
231 j++;
232 } else if (!strcasecmp(c->argv[j]->ptr,"by") && leftargs >= 1) {
233 sortby = c->argv[j+1];
234 /* If the BY pattern does not contain '*', i.e. it is constant,
235 * we don't need to sort nor to lookup the weight keys. */
236 if (strchr(c->argv[j+1]->ptr,'*') == NULL((void*)0)) {
237 dontsort = 1;
238 } else {
239 /* If BY is specified with a real patter, we can't accept
240 * it in cluster mode. */
241 if (server.cluster_enabled) {
242 addReplyError(c,"BY option of SORT denied in Cluster mode.");
243 syntax_error++;
244 break;
245 }
246 }
247 j++;
248 } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) {
249 if (server.cluster_enabled) {
250 addReplyError(c,"GET option of SORT denied in Cluster mode.");
251 syntax_error++;
252 break;
253 }
254 listAddNodeTail(operations,createSortOperation(
255 SORT_OP_GET0,c->argv[j+1]));
256 getop++;
257 j++;
258 } else {
259 addReplyErrorObject(c,shared.syntaxerr);
260 syntax_error++;
261 break;
262 }
263 j++;
264 }
265
266 /* Handle syntax errors set during options parsing. */
267 if (syntax_error) {
268 listRelease(operations);
269 return;
270 }
271
272 /* Lookup the key to sort. It must be of the right types */
273 if (!storekey)
274 sortval = lookupKeyRead(c->db,c->argv[1]);
275 else
276 sortval = lookupKeyWrite(c->db,c->argv[1]);
277 if (sortval && sortval->type != OBJ_SET2 &&
278 sortval->type != OBJ_LIST1 &&
279 sortval->type != OBJ_ZSET3)
280 {
281 listRelease(operations);
282 addReplyErrorObject(c,shared.wrongtypeerr);
283 return;
284 }
285
286 /* Now we need to protect sortval incrementing its count, in the future
287 * SORT may have options able to overwrite/delete keys during the sorting
288 * and the sorted key itself may get destroyed */
289 if (sortval)
290 incrRefCount(sortval);
291 else
292 sortval = createQuicklistObject();
293
294
295 /* When sorting a set with no sort specified, we must sort the output
296 * so the result is consistent across scripting and replication.
297 *
298 * The other types (list, sorted set) will retain their native order
299 * even if no sort order is requested, so they remain stable across
300 * scripting and replication. */
301 if (dontsort &&
302 sortval->type == OBJ_SET2 &&
303 (storekey || c->flags & CLIENT_LUA(1<<8)))
304 {
305 /* Force ALPHA sorting */
306 dontsort = 0;
307 alpha = 1;
308 sortby = NULL((void*)0);
309 }
310
311 /* Destructively convert encoded sorted sets for SORT. */
312 if (sortval->type == OBJ_ZSET3)
313 zsetConvert(sortval, OBJ_ENCODING_SKIPLIST7);
314
315 /* Objtain the length of the object to sort. */
316 switch(sortval->type) {
317 case OBJ_LIST1: vectorlen = listTypeLength(sortval); break;
318 case OBJ_SET2: vectorlen = setTypeSize(sortval); break;
319 case OBJ_ZSET3: vectorlen = dictSize(((zset*)sortval->ptr)->dict)((((zset*)sortval->ptr)->dict)->ht[0].used+(((zset*)
sortval->ptr)->dict)->ht[1].used)
; break;
320 default: vectorlen = 0; serverPanic("Bad SORT type")_serverPanic("sort.c",320,"Bad SORT type"),__builtin_unreachable
()
; /* Avoid GCC warning */
Value stored to 'vectorlen' is never read
321 }
322
323 /* Perform LIMIT start,count sanity checking. */
324 start = (limit_start < 0) ? 0 : limit_start;
325 end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
326 if (start >= vectorlen) {
327 start = vectorlen-1;
328 end = vectorlen-2;
329 }
330 if (end >= vectorlen) end = vectorlen-1;
331
332 /* Whenever possible, we load elements into the output array in a more
333 * direct way. This is possible if:
334 *
335 * 1) The object to sort is a sorted set or a list (internally sorted).
336 * 2) There is nothing to sort as dontsort is true (BY <constant string>).
337 *
338 * In this special case, if we have a LIMIT option that actually reduces
339 * the number of elements to fetch, we also optimize to just load the
340 * range we are interested in and allocating a vector that is big enough
341 * for the selected range length. */
342 if ((sortval->type == OBJ_ZSET3 || sortval->type == OBJ_LIST1) &&
343 dontsort &&
344 (start != 0 || end != vectorlen-1))
345 {
346 vectorlen = end-start+1;
347 }
348
349 /* Load the sorting vector with all the objects to sort */
350 vector = zmalloc(sizeof(redisSortObject)*vectorlen);
351 j = 0;
352
353 if (sortval->type == OBJ_LIST1 && dontsort) {
354 /* Special handling for a list, if 'dontsort' is true.
355 * This makes sure we return elements in the list original
356 * ordering, accordingly to DESC / ASC options.
357 *
358 * Note that in this case we also handle LIMIT here in a direct
359 * way, just getting the required range, as an optimization. */
360 if (end >= start) {
361 listTypeIterator *li;
362 listTypeEntry entry;
363 li = listTypeInitIterator(sortval,
364 desc ? (long)(listTypeLength(sortval) - start - 1) : start,
365 desc ? LIST_HEAD0 : LIST_TAIL1);
366
367 while(j < vectorlen && listTypeNext(li,&entry)) {
368 vector[j].obj = listTypeGet(&entry);
369 vector[j].u.score = 0;
370 vector[j].u.cmpobj = NULL((void*)0);
371 j++;
372 }
373 listTypeReleaseIterator(li);
374 /* Fix start/end: output code is not aware of this optimization. */
375 end -= start;
376 start = 0;
377 }
378 } else if (sortval->type == OBJ_LIST1) {
379 listTypeIterator *li = listTypeInitIterator(sortval,0,LIST_TAIL1);
380 listTypeEntry entry;
381 while(listTypeNext(li,&entry)) {
382 vector[j].obj = listTypeGet(&entry);
383 vector[j].u.score = 0;
384 vector[j].u.cmpobj = NULL((void*)0);
385 j++;
386 }
387 listTypeReleaseIterator(li);
388 } else if (sortval->type == OBJ_SET2) {
389 setTypeIterator *si = setTypeInitIterator(sortval);
390 sds sdsele;
391 while((sdsele = setTypeNextObject(si)) != NULL((void*)0)) {
392 vector[j].obj = createObject(OBJ_STRING0,sdsele);
393 vector[j].u.score = 0;
394 vector[j].u.cmpobj = NULL((void*)0);
395 j++;
396 }
397 setTypeReleaseIterator(si);
398 } else if (sortval->type == OBJ_ZSET3 && dontsort) {
399 /* Special handling for a sorted set, if 'dontsort' is true.
400 * This makes sure we return elements in the sorted set original
401 * ordering, accordingly to DESC / ASC options.
402 *
403 * Note that in this case we also handle LIMIT here in a direct
404 * way, just getting the required range, as an optimization. */
405
406 zset *zs = sortval->ptr;
407 zskiplist *zsl = zs->zsl;
408 zskiplistNode *ln;
409 sds sdsele;
410 int rangelen = vectorlen;
411
412 /* Check if starting point is trivial, before doing log(N) lookup. */
413 if (desc) {
414 long zsetlen = dictSize(((zset*)sortval->ptr)->dict)((((zset*)sortval->ptr)->dict)->ht[0].used+(((zset*)
sortval->ptr)->dict)->ht[1].used)
;
415
416 ln = zsl->tail;
417 if (start > 0)
418 ln = zslGetElementByRank(zsl,zsetlen-start);
419 } else {
420 ln = zsl->header->level[0].forward;
421 if (start > 0)
422 ln = zslGetElementByRank(zsl,start+1);
423 }
424
425 while(rangelen--) {
426 serverAssertWithInfo(c,sortval,ln != NULL)((ln != ((void*)0))?(void)0 : (_serverAssertWithInfo(c,sortval
,"ln != NULL","sort.c",426),__builtin_unreachable()))
;
427 sdsele = ln->ele;
428 vector[j].obj = createStringObject(sdsele,sdslen(sdsele));
429 vector[j].u.score = 0;
430 vector[j].u.cmpobj = NULL((void*)0);
431 j++;
432 ln = desc ? ln->backward : ln->level[0].forward;
433 }
434 /* Fix start/end: output code is not aware of this optimization. */
435 end -= start;
436 start = 0;
437 } else if (sortval->type == OBJ_ZSET3) {
438 dict *set = ((zset*)sortval->ptr)->dict;
439 dictIterator *di;
440 dictEntry *setele;
441 sds sdsele;
442 di = dictGetIterator(set);
443 while((setele = dictNext(di)) != NULL((void*)0)) {
444 sdsele = dictGetKey(setele)((setele)->key);
445 vector[j].obj = createStringObject(sdsele,sdslen(sdsele));
446 vector[j].u.score = 0;
447 vector[j].u.cmpobj = NULL((void*)0);
448 j++;
449 }
450 dictReleaseIterator(di);
451 } else {
452 serverPanic("Unknown type")_serverPanic("sort.c",452,"Unknown type"),__builtin_unreachable
()
;
453 }
454 serverAssertWithInfo(c,sortval,j == vectorlen)((j == vectorlen)?(void)0 : (_serverAssertWithInfo(c,sortval,
"j == vectorlen","sort.c",454),__builtin_unreachable()))
;
455
456 /* Now it's time to load the right scores in the sorting vector */
457 if (!dontsort) {
458 for (j = 0; j < vectorlen; j++) {
459 robj *byval;
460 if (sortby) {
461 /* lookup value to sort by */
462 byval = lookupKeyByPattern(c->db,sortby,vector[j].obj,storekey!=NULL((void*)0));
463 if (!byval) continue;
464 } else {
465 /* use object itself to sort by */
466 byval = vector[j].obj;
467 }
468
469 if (alpha) {
470 if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);
471 } else {
472 if (sdsEncodedObject(byval)(byval->encoding == 0 || byval->encoding == 8)) {
473 char *eptr;
474
475 vector[j].u.score = strtod(byval->ptr,&eptr);
476 if (eptr[0] != '\0' || errno(*__errno_location ()) == ERANGE34 ||
477 isnan(vector[j].u.score)__builtin_isnan (vector[j].u.score))
478 {
479 int_conversion_error = 1;
480 }
481 } else if (byval->encoding == OBJ_ENCODING_INT1) {
482 /* Don't need to decode the object if it's
483 * integer-encoded (the only encoding supported) so
484 * far. We can just cast it */
485 vector[j].u.score = (long)byval->ptr;
486 } else {
487 serverAssertWithInfo(c,sortval,1 != 1)((1 != 1)?(void)0 : (_serverAssertWithInfo(c,sortval,"1 != 1"
,"sort.c",487),__builtin_unreachable()))
;
488 }
489 }
490
491 /* when the object was retrieved using lookupKeyByPattern,
492 * its refcount needs to be decreased. */
493 if (sortby) {
494 decrRefCount(byval);
495 }
496 }
497
498 server.sort_desc = desc;
499 server.sort_alpha = alpha;
500 server.sort_bypattern = sortby ? 1 : 0;
501 server.sort_store = storekey ? 1 : 0;
502 if (sortby && (start != 0 || end != vectorlen-1))
503 pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end);
504 else
505 qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare);
506 }
507
508 /* Send command output to the output buffer, performing the specified
509 * GET/DEL/INCR/DECR operations if any. */
510 outputlen = getop ? getop*(end-start+1) : end-start+1;
511 if (int_conversion_error) {
512 addReplyError(c,"One or more scores can't be converted into double");
513 } else if (storekey == NULL((void*)0)) {
514 /* STORE option not specified, sent the sorting result to client */
515 addReplyArrayLen(c,outputlen);
516 for (j = start; j <= end; j++) {
517 listNode *ln;
518 listIter li;
519
520 if (!getop) addReplyBulk(c,vector[j].obj);
521 listRewind(operations,&li);
522 while((ln = listNext(&li))) {
523 redisSortOperation *sop = ln->value;
524 robj *val = lookupKeyByPattern(c->db,sop->pattern,
525 vector[j].obj,storekey!=NULL((void*)0));
526
527 if (sop->type == SORT_OP_GET0) {
528 if (!val) {
529 addReplyNull(c);
530 } else {
531 addReplyBulk(c,val);
532 decrRefCount(val);
533 }
534 } else {
535 /* Always fails */
536 serverAssertWithInfo(c,sortval,sop->type == SORT_OP_GET)((sop->type == 0)?(void)0 : (_serverAssertWithInfo(c,sortval
,"sop->type == SORT_OP_GET","sort.c",536),__builtin_unreachable
()))
;
537 }
538 }
539 }
540 } else {
541 robj *sobj = createQuicklistObject();
542
543 /* STORE option specified, set the sorting result as a List object */
544 for (j = start; j <= end; j++) {
545 listNode *ln;
546 listIter li;
547
548 if (!getop) {
549 listTypePush(sobj,vector[j].obj,LIST_TAIL1);
550 } else {
551 listRewind(operations,&li);
552 while((ln = listNext(&li))) {
553 redisSortOperation *sop = ln->value;
554 robj *val = lookupKeyByPattern(c->db,sop->pattern,
555 vector[j].obj,storekey!=NULL((void*)0));
556
557 if (sop->type == SORT_OP_GET0) {
558 if (!val) val = createStringObject("",0);
559
560 /* listTypePush does an incrRefCount, so we should take care
561 * care of the incremented refcount caused by either
562 * lookupKeyByPattern or createStringObject("",0) */
563 listTypePush(sobj,val,LIST_TAIL1);
564 decrRefCount(val);
565 } else {
566 /* Always fails */
567 serverAssertWithInfo(c,sortval,sop->type == SORT_OP_GET)((sop->type == 0)?(void)0 : (_serverAssertWithInfo(c,sortval
,"sop->type == SORT_OP_GET","sort.c",567),__builtin_unreachable
()))
;
568 }
569 }
570 }
571 }
572 if (outputlen) {
573 setKey(c,c->db,storekey,sobj);
574 notifyKeyspaceEvent(NOTIFY_LIST(1<<4),"sortstore",storekey,
575 c->db->id);
576 server.dirty += outputlen;
577 } else if (dbDelete(c->db,storekey)) {
578 signalModifiedKey(c,c->db,storekey);
579 notifyKeyspaceEvent(NOTIFY_GENERIC(1<<2),"del",storekey,c->db->id);
580 server.dirty++;
581 }
582 decrRefCount(sobj);
583 addReplyLongLong(c,outputlen);
584 }
585
586 /* Cleanup */
587 for (j = 0; j < vectorlen; j++)
588 decrRefCount(vector[j].obj);
589
590 decrRefCount(sortval);
591 listRelease(operations);
592 for (j = 0; j < vectorlen; j++) {
593 if (alpha && vector[j].u.cmpobj)
594 decrRefCount(vector[j].u.cmpobj);
595 }
596 zfree(vector);
597}