LCOV - code coverage report
Current view: directory - redis/src - t_set.c (source / functions) Found Hit Coverage
Test: redis.info Lines: 322 310 96.3 %
Date: 2012-04-04 Functions: 27 27 100.0 %
Colors: not hit hit

       1                 : #include "redis.h"
       2                 : 
       3                 : /*-----------------------------------------------------------------------------
       4                 :  * Set Commands
       5                 :  *----------------------------------------------------------------------------*/
       6                 : 
       7                 : /* Factory method to return a set that *can* hold "value". When the object has
       8                 :  * an integer-encodable value, an intset will be returned. Otherwise a regular
       9                 :  * hash table. */
      10           14555 : robj *setTypeCreate(robj *value) {
      11           14555 :     if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK)
      12           10951 :         return createIntsetObject();
      13            3604 :     return createSetObject();
      14                 : }
      15                 : 
      16           57782 : int setTypeAdd(robj *subject, robj *value) {
      17                 :     long long llval;
      18           57782 :     if (subject->encoding == REDIS_ENCODING_HT) {
      19           24533 :         if (dictAdd(subject->ptr,value,NULL) == DICT_OK) {
      20           23801 :             incrRefCount(value);
      21           23801 :             return 1;
      22                 :         }
      23           33249 :     } else if (subject->encoding == REDIS_ENCODING_INTSET) {
      24           33249 :         if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
      25           31463 :             uint8_t success = 0;
      26           31463 :             subject->ptr = intsetAdd(subject->ptr,llval,&success);
      27           31463 :             if (success) {
      28                 :                 /* Convert to regular set when the intset contains
      29                 :                  * too many entries. */
      30           29463 :                 if (intsetLen(subject->ptr) > server.set_max_intset_entries)
      31              16 :                     setTypeConvert(subject,REDIS_ENCODING_HT);
      32           29463 :                 return 1;
      33                 :             }
      34                 :         } else {
      35                 :             /* Failed to get integer from object, convert to regular set. */
      36            1786 :             setTypeConvert(subject,REDIS_ENCODING_HT);
      37                 : 
      38                 :             /* The set *was* an intset and this value is not integer
      39                 :              * encodable, so dictAdd should always work. */
      40            1786 :             redisAssertWithInfo(NULL,value,dictAdd(subject->ptr,value,NULL) == DICT_OK);
      41            1786 :             incrRefCount(value);
      42            1786 :             return 1;
      43                 :         }
      44                 :     } else {
      45               0 :         redisPanic("Unknown set encoding");
      46                 :     }
      47            2732 :     return 0;
      48                 : }
      49                 : 
      50           16369 : int setTypeRemove(robj *setobj, robj *value) {
      51                 :     long long llval;
      52           16369 :     if (setobj->encoding == REDIS_ENCODING_HT) {
      53           10124 :         if (dictDelete(setobj->ptr,value) == DICT_OK) {
      54            9203 :             if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr);
      55            9203 :             return 1;
      56                 :         }
      57            6245 :     } else if (setobj->encoding == REDIS_ENCODING_INTSET) {
      58            6245 :         if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
      59                 :             int success;
      60            5674 :             setobj->ptr = intsetRemove(setobj->ptr,llval,&success);
      61            5674 :             if (success) return 1;
      62                 :         }
      63                 :     } else {
      64               0 :         redisPanic("Unknown set encoding");
      65                 :     }
      66            3096 :     return 0;
      67                 : }
      68                 : 
      69            1590 : int setTypeIsMember(robj *subject, robj *value) {
      70                 :     long long llval;
      71            1590 :     if (subject->encoding == REDIS_ENCODING_HT) {
      72            1228 :         return dictFind((dict*)subject->ptr,value) != NULL;
      73             362 :     } else if (subject->encoding == REDIS_ENCODING_INTSET) {
      74             362 :         if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
      75               3 :             return intsetFind((intset*)subject->ptr,llval);
      76                 :         }
      77                 :     } else {
      78               0 :         redisPanic("Unknown set encoding");
      79                 :     }
      80             359 :     return 0;
      81                 : }
      82                 : 
      83           50327 : setTypeIterator *setTypeInitIterator(robj *subject) {
      84           50327 :     setTypeIterator *si = zmalloc(sizeof(setTypeIterator));
      85           50327 :     si->subject = subject;
      86           50327 :     si->encoding = subject->encoding;
      87           50327 :     if (si->encoding == REDIS_ENCODING_HT) {
      88           14392 :         si->di = dictGetIterator(subject->ptr);
      89           35935 :     } else if (si->encoding == REDIS_ENCODING_INTSET) {
      90           35935 :         si->ii = 0;
      91                 :     } else {
      92               0 :         redisPanic("Unknown set encoding");
      93                 :     }
      94           50327 :     return si;
      95                 : }
      96                 : 
      97           50327 : void setTypeReleaseIterator(setTypeIterator *si) {
      98           50327 :     if (si->encoding == REDIS_ENCODING_HT)
      99           14392 :         dictReleaseIterator(si->di);
     100           50327 :     zfree(si);
     101           50327 : }
     102                 : 
     103                 : /* Move to the next entry in the set. Returns the object at the current
     104                 :  * position.
     105                 :  *
     106                 :  * Since set elements can be internally be stored as redis objects or
     107                 :  * simple arrays of integers, setTypeNext returns the encoding of the
     108                 :  * set object you are iterating, and will populate the appropriate pointer
     109                 :  * (eobj) or (llobj) accordingly.
     110                 :  *
     111                 :  * When there are no longer elements -1 is returned.
     112                 :  * Returned objects ref count is not incremented, so this function is
     113                 :  * copy on write friendly. */
     114          178254 : int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) {
     115          178254 :     if (si->encoding == REDIS_ENCODING_HT) {
     116           85483 :         dictEntry *de = dictNext(si->di);
     117           85483 :         if (de == NULL) return -1;
     118           71091 :         *objele = dictGetKey(de);
     119           92771 :     } else if (si->encoding == REDIS_ENCODING_INTSET) {
     120           92771 :         if (!intsetGet(si->subject->ptr,si->ii++,llele))
     121           35935 :             return -1;
     122                 :     }
     123          127927 :     return si->encoding;
     124                 : }
     125                 : 
     126                 : /* The not copy on write friendly version but easy to use version
     127                 :  * of setTypeNext() is setTypeNextObject(), returning new objects
     128                 :  * or incrementing the ref count of returned objects. So if you don't
     129                 :  * retain a pointer to this object you should call decrRefCount() against it.
     130                 :  *
     131                 :  * This function is the way to go for write operations where COW is not
     132                 :  * an issue as the result will be anyway of incrementing the ref count. */
     133          147738 : robj *setTypeNextObject(setTypeIterator *si) {
     134                 :     int64_t intele;
     135                 :     robj *objele;
     136                 :     int encoding;
     137                 : 
     138          147738 :     encoding = setTypeNext(si,&objele,&intele);
     139          147738 :     switch(encoding) {
     140           46109 :         case -1:    return NULL;
     141                 :         case REDIS_ENCODING_INTSET:
     142           40883 :             return createStringObjectFromLongLong(intele);
     143                 :         case REDIS_ENCODING_HT:
     144           60746 :             incrRefCount(objele);
     145           60746 :             return objele;
     146                 :         default:
     147               0 :             redisPanic("Unsupported encoding");
     148                 :     }
     149                 :     return NULL; /* just to suppress warnings */
     150                 : }
     151                 : 
     152                 : /* Return random element from a non empty set.
     153                 :  * The returned element can be a int64_t value if the set is encoded
     154                 :  * as an "intset" blob of integers, or a redis object if the set
     155                 :  * is a regular set.
     156                 :  *
     157                 :  * The caller provides both pointers to be populated with the right
     158                 :  * object. The return value of the function is the object->encoding
     159                 :  * field of the object and is used by the caller to check if the
     160                 :  * int64_t pointer or the redis object pointere was populated.
     161                 :  *
     162                 :  * When an object is returned (the set was a real set) the ref count
     163                 :  * of the object is not incremented so this function can be considered
     164                 :  * copy on write friendly. */
     165           10606 : int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) {
     166           10606 :     if (setobj->encoding == REDIS_ENCODING_HT) {
     167            7562 :         dictEntry *de = dictGetRandomKey(setobj->ptr);
     168            7562 :         *objele = dictGetKey(de);
     169            3044 :     } else if (setobj->encoding == REDIS_ENCODING_INTSET) {
     170            3044 :         *llele = intsetRandom(setobj->ptr);
     171                 :     } else {
     172               0 :         redisPanic("Unknown set encoding");
     173                 :     }
     174           10606 :     return setobj->encoding;
     175                 : }
     176                 : 
     177           29190 : unsigned long setTypeSize(robj *subject) {
     178           29190 :     if (subject->encoding == REDIS_ENCODING_HT) {
     179           12885 :         return dictSize((dict*)subject->ptr);
     180           16305 :     } else if (subject->encoding == REDIS_ENCODING_INTSET) {
     181           16305 :         return intsetLen((intset*)subject->ptr);
     182                 :     } else {
     183               0 :         redisPanic("Unknown set encoding");
     184                 :     }
     185                 : }
     186                 : 
     187                 : /* Convert the set to specified encoding. The resulting dict (when converting
     188                 :  * to a hashtable) is presized to hold the number of elements in the original
     189                 :  * set. */
     190            2002 : void setTypeConvert(robj *setobj, int enc) {
     191                 :     setTypeIterator *si;
     192            2002 :     redisAssertWithInfo(NULL,setobj,setobj->type == REDIS_SET &&
     193                 :                              setobj->encoding == REDIS_ENCODING_INTSET);
     194                 : 
     195            2002 :     if (enc == REDIS_ENCODING_HT) {
     196                 :         int64_t intele;
     197            2002 :         dict *d = dictCreate(&setDictType,NULL);
     198                 :         robj *element;
     199                 : 
     200                 :         /* Presize the dict to avoid rehashing */
     201            2002 :         dictExpand(d,intsetLen(setobj->ptr));
     202                 : 
     203                 :         /* To add the elements we extract integers and create redis objects */
     204            2002 :         si = setTypeInitIterator(setobj);
     205           13152 :         while (setTypeNext(si,NULL,&intele) != -1) {
     206            9148 :             element = createStringObjectFromLongLong(intele);
     207            9148 :             redisAssertWithInfo(NULL,element,dictAdd(d,element,NULL) == DICT_OK);
     208                 :         }
     209            2002 :         setTypeReleaseIterator(si);
     210                 : 
     211            2002 :         setobj->encoding = REDIS_ENCODING_HT;
     212            2002 :         zfree(setobj->ptr);
     213            2002 :         setobj->ptr = d;
     214                 :     } else {
     215               0 :         redisPanic("Unsupported set conversion");
     216                 :     }
     217            2002 : }
     218                 : 
     219           45177 : void saddCommand(redisClient *c) {
     220                 :     robj *set;
     221           45177 :     int j, added = 0;
     222                 : 
     223           45177 :     set = lookupKeyWrite(c->db,c->argv[1]);
     224           45177 :     if (set == NULL) {
     225           14553 :         set = setTypeCreate(c->argv[2]);
     226           14553 :         dbAdd(c->db,c->argv[1],set);
     227                 :     } else {
     228           30624 :         if (set->type != REDIS_SET) {
     229               1 :             addReply(c,shared.wrongtypeerr);
     230               1 :             return;
     231                 :         }
     232                 :     }
     233                 : 
     234           92408 :     for (j = 2; j < c->argc; j++) {
     235           47232 :         c->argv[j] = tryObjectEncoding(c->argv[j]);
     236           47232 :         if (setTypeAdd(set,c->argv[j])) added++;
     237                 :     }
     238           45176 :     if (added) signalModifiedKey(c->db,c->argv[1]);
     239           45176 :     server.dirty += added;
     240           45176 :     addReplyLongLong(c,added);
     241                 : }
     242                 : 
     243            5221 : void sremCommand(redisClient *c) {
     244                 :     robj *set;
     245            5221 :     int j, deleted = 0;
     246                 : 
     247           10442 :     if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
     248            5221 :         checkType(c,set,REDIS_SET)) return;
     249                 : 
     250            5836 :     for (j = 2; j < c->argc; j++) {
     251            5228 :         if (setTypeRemove(set,c->argv[j])) {
     252            4619 :             deleted++;
     253            4619 :             if (setTypeSize(set) == 0) {
     254            4613 :                 dbDelete(c->db,c->argv[1]);
     255            4613 :                 break;
     256                 :             }
     257                 :         }
     258                 :     }
     259            5221 :     if (deleted) {
     260            4616 :         signalModifiedKey(c->db,c->argv[1]);
     261            4616 :         server.dirty += deleted;
     262                 :     }
     263            5221 :     addReplyLongLong(c,deleted);
     264                 : }
     265                 : 
     266               9 : void smoveCommand(redisClient *c) {
     267                 :     robj *srcset, *dstset, *ele;
     268               9 :     srcset = lookupKeyWrite(c->db,c->argv[1]);
     269               9 :     dstset = lookupKeyWrite(c->db,c->argv[2]);
     270               9 :     ele = c->argv[3] = tryObjectEncoding(c->argv[3]);
     271                 : 
     272                 :     /* If the source key does not exist return 0 */
     273               9 :     if (srcset == NULL) {
     274               1 :         addReply(c,shared.czero);
     275               1 :         return;
     276                 :     }
     277                 : 
     278                 :     /* If the source key has the wrong type, or the destination key
     279                 :      * is set and has the wrong type, return with an error. */
     280              13 :     if (checkType(c,srcset,REDIS_SET) ||
     281               5 :         (dstset && checkType(c,dstset,REDIS_SET))) return;
     282                 : 
     283                 :     /* If srcset and dstset are equal, SMOVE is a no-op */
     284               6 :     if (srcset == dstset) {
     285               0 :         addReply(c,shared.cone);
     286               0 :         return;
     287                 :     }
     288                 : 
     289                 :     /* If the element cannot be removed from the src set, return 0. */
     290               6 :     if (!setTypeRemove(srcset,ele)) {
     291               1 :         addReply(c,shared.czero);
     292               1 :         return;
     293                 :     }
     294                 : 
     295                 :     /* Remove the src set from the database when empty */
     296               5 :     if (setTypeSize(srcset) == 0) dbDelete(c->db,c->argv[1]);
     297               5 :     signalModifiedKey(c->db,c->argv[1]);
     298               5 :     signalModifiedKey(c->db,c->argv[2]);
     299               5 :     server.dirty++;
     300                 : 
     301                 :     /* Create the destination set when it doesn't exist */
     302               5 :     if (!dstset) {
     303               2 :         dstset = setTypeCreate(ele);
     304               2 :         dbAdd(c->db,c->argv[2],dstset);
     305                 :     }
     306                 : 
     307                 :     /* An extra key has changed when ele was successfully added to dstset */
     308               5 :     if (setTypeAdd(dstset,ele)) server.dirty++;
     309               5 :     addReply(c,shared.cone);
     310                 : }
     311                 : 
     312               7 : void sismemberCommand(redisClient *c) {
     313                 :     robj *set;
     314                 : 
     315              14 :     if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
     316               7 :         checkType(c,set,REDIS_SET)) return;
     317                 : 
     318               7 :     c->argv[2] = tryObjectEncoding(c->argv[2]);
     319               7 :     if (setTypeIsMember(set,c->argv[2]))
     320               5 :         addReply(c,shared.cone);
     321                 :     else
     322               2 :         addReply(c,shared.czero);
     323                 : }
     324                 : 
     325              25 : void scardCommand(redisClient *c) {
     326                 :     robj *o;
     327                 : 
     328              28 :     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
     329               3 :         checkType(c,o,REDIS_SET)) return;
     330                 : 
     331               3 :     addReplyLongLong(c,setTypeSize(o));
     332                 : }
     333                 : 
     334           10406 : void spopCommand(redisClient *c) {
     335                 :     robj *set, *ele, *aux;
     336                 :     int64_t llele;
     337                 :     int encoding;
     338                 : 
     339           20812 :     if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
     340           10406 :         checkType(c,set,REDIS_SET)) return;
     341                 : 
     342           10406 :     encoding = setTypeRandomElement(set,&ele,&llele);
     343           10406 :     if (encoding == REDIS_ENCODING_INTSET) {
     344            2944 :         ele = createStringObjectFromLongLong(llele);
     345            2944 :         set->ptr = intsetRemove(set->ptr,llele,NULL);
     346                 :     } else {
     347            7462 :         incrRefCount(ele);
     348            7462 :         setTypeRemove(set,ele);
     349                 :     }
     350                 : 
     351                 :     /* Replicate/AOF this command as an SREM operation */
     352           10406 :     aux = createStringObject("SREM",4);
     353           10406 :     rewriteClientCommandVector(c,3,aux,c->argv[1],ele);
     354           10406 :     decrRefCount(ele);
     355           10406 :     decrRefCount(aux);
     356                 : 
     357           10406 :     addReplyBulk(c,ele);
     358           10406 :     if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
     359           10406 :     signalModifiedKey(c->db,c->argv[1]);
     360           10406 :     server.dirty++;
     361                 : }
     362                 : 
     363             200 : void srandmemberCommand(redisClient *c) {
     364                 :     robj *set, *ele;
     365                 :     int64_t llele;
     366                 :     int encoding;
     367                 : 
     368             400 :     if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
     369             200 :         checkType(c,set,REDIS_SET)) return;
     370                 : 
     371             200 :     encoding = setTypeRandomElement(set,&ele,&llele);
     372             200 :     if (encoding == REDIS_ENCODING_INTSET) {
     373             100 :         addReplyBulkLongLong(c,llele);
     374                 :     } else {
     375             100 :         addReplyBulk(c,ele);
     376                 :     }
     377                 : }
     378                 : 
     379            2029 : int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
     380            2029 :     return setTypeSize(*(robj**)s1)-setTypeSize(*(robj**)s2);
     381                 : }
     382                 : 
     383            2218 : void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) {
     384            2218 :     robj **sets = zmalloc(sizeof(robj*)*setnum);
     385                 :     setTypeIterator *si;
     386            2218 :     robj *eleobj, *dstset = NULL;
     387                 :     int64_t intobj;
     388            2218 :     void *replylen = NULL;
     389            2218 :     unsigned long j, cardinality = 0;
     390                 :     int encoding;
     391                 : 
     392            6459 :     for (j = 0; j < setnum; j++) {
     393                 :         robj *setobj = dstkey ?
     394            4037 :             lookupKeyWrite(c->db,setkeys[j]) :
     395            8280 :             lookupKeyRead(c->db,setkeys[j]);
     396            4243 :         if (!setobj) {
     397               1 :             zfree(sets);
     398               1 :             if (dstkey) {
     399               1 :                 if (dbDelete(c->db,dstkey)) {
     400               1 :                     signalModifiedKey(c->db,dstkey);
     401               1 :                     server.dirty++;
     402                 :                 }
     403               1 :                 addReply(c,shared.czero);
     404                 :             } else {
     405               0 :                 addReply(c,shared.emptymultibulk);
     406                 :             }
     407                 :             return;
     408                 :         }
     409            4242 :         if (checkType(c,setobj,REDIS_SET)) {
     410               1 :             zfree(sets);
     411               1 :             return;
     412                 :         }
     413            4241 :         sets[j] = setobj;
     414                 :     }
     415                 :     /* Sort sets from the smallest to largest, this will improve our
     416                 :      * algorithm's performace */
     417            2216 :     qsort(sets,setnum,sizeof(robj*),qsortCompareSetsByCardinality);
     418                 : 
     419                 :     /* The first thing we should output is the total number of elements...
     420                 :      * since this is a multi-bulk write, but at this stage we don't know
     421                 :      * the intersection set size, so we use a trick, append an empty object
     422                 :      * to the output list and save the pointer to later modify it with the
     423                 :      * right length */
     424            2216 :     if (!dstkey) {
     425             199 :         replylen = addDeferredMultiBulkLength(c);
     426                 :     } else {
     427                 :         /* If we have a target key where to store the resulting set
     428                 :          * create this key with an empty set inside */
     429            2017 :         dstset = createIntsetObject();
     430                 :     }
     431                 : 
     432                 :     /* Iterate all the elements of the first (smallest) set, and test
     433                 :      * the element against all the other sets, if at least one set does
     434                 :      * not include the element it is discarded */
     435            2216 :     si = setTypeInitIterator(sets[0]);
     436           21582 :     while((encoding = setTypeNext(si,&eleobj,&intobj)) != -1) {
     437           17214 :         for (j = 1; j < setnum; j++) {
     438            3272 :             if (sets[j] == sets[0]) continue;
     439            3268 :             if (encoding == REDIS_ENCODING_INTSET) {
     440                 :                 /* intset with intset is simple... and fast */
     441            3786 :                 if (sets[j]->encoding == REDIS_ENCODING_INTSET &&
     442            1681 :                     !intsetFind((intset*)sets[j]->ptr,intobj))
     443                 :                 {
     444                 :                     break;
     445                 :                 /* in order to compare an integer with an object we
     446                 :                  * have to use the generic function, creating an object
     447                 :                  * for this */
     448             454 :                 } else if (sets[j]->encoding == REDIS_ENCODING_HT) {
     449             424 :                     eleobj = createStringObjectFromLongLong(intobj);
     450             424 :                     if (!setTypeIsMember(sets[j],eleobj)) {
     451             424 :                         decrRefCount(eleobj);
     452             424 :                         break;
     453                 :                     }
     454               0 :                     decrRefCount(eleobj);
     455                 :                 }
     456            1163 :             } else if (encoding == REDIS_ENCODING_HT) {
     457                 :                 /* Optimization... if the source object is integer
     458                 :                  * encoded AND the target set is an intset, we can get
     459                 :                  * a much faster path. */
     460            1789 :                 if (eleobj->encoding == REDIS_ENCODING_INT &&
     461             622 :                     sets[j]->encoding == REDIS_ENCODING_INTSET &&
     462               4 :                     !intsetFind((intset*)sets[j]->ptr,(long)eleobj->ptr))
     463                 :                 {
     464                 :                     break;
     465                 :                 /* else... object to object check is easy as we use the
     466                 :                  * type agnostic API here. */
     467            1159 :                 } else if (!setTypeIsMember(sets[j],eleobj)) {
     468                 :                     break;
     469                 :                 }
     470                 :             }
     471                 :         }
     472                 : 
     473                 :         /* Only take action when all sets contain the member */
     474           17150 :         if (j == setnum) {
     475           13942 :             if (!dstkey) {
     476           13908 :                 if (encoding == REDIS_ENCODING_HT)
     477            9197 :                     addReplyBulk(c,eleobj);
     478                 :                 else
     479            4711 :                     addReplyBulkLongLong(c,intobj);
     480           13908 :                 cardinality++;
     481                 :             } else {
     482              34 :                 if (encoding == REDIS_ENCODING_INTSET) {
     483              19 :                     eleobj = createStringObjectFromLongLong(intobj);
     484              19 :                     setTypeAdd(dstset,eleobj);
     485              19 :                     decrRefCount(eleobj);
     486                 :                 } else {
     487              15 :                     setTypeAdd(dstset,eleobj);
     488                 :                 }
     489                 :             }
     490                 :         }
     491                 :     }
     492            2216 :     setTypeReleaseIterator(si);
     493                 : 
     494            2216 :     if (dstkey) {
     495                 :         /* Store the resulting set into the target, if the intersection
     496                 :          * is not an empty set. */
     497            2017 :         dbDelete(c->db,dstkey);
     498            2017 :         if (setTypeSize(dstset) > 0) {
     499              10 :             dbAdd(c->db,dstkey,dstset);
     500              10 :             addReplyLongLong(c,setTypeSize(dstset));
     501                 :         } else {
     502            2007 :             decrRefCount(dstset);
     503            2007 :             addReply(c,shared.czero);
     504                 :         }
     505            2017 :         signalModifiedKey(c->db,dstkey);
     506            2017 :         server.dirty++;
     507                 :     } else {
     508             199 :         setDeferredMultiBulkLength(c,replylen,cardinality);
     509                 :     }
     510            2216 :     zfree(sets);
     511                 : }
     512                 : 
     513             200 : void sinterCommand(redisClient *c) {
     514             200 :     sinterGenericCommand(c,c->argv+1,c->argc-1,NULL);
     515             200 : }
     516                 : 
     517            2018 : void sinterstoreCommand(redisClient *c) {
     518            2018 :     sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);
     519            2018 : }
     520                 : 
     521                 : #define REDIS_OP_UNION 0
     522                 : #define REDIS_OP_DIFF 1
     523                 : #define REDIS_OP_INTER 2
     524                 : 
     525            4041 : void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *dstkey, int op) {
     526            4041 :     robj **sets = zmalloc(sizeof(robj*)*setnum);
     527                 :     setTypeIterator *si;
     528            4041 :     robj *ele, *dstset = NULL;
     529            4041 :     int j, cardinality = 0;
     530                 : 
     531           12129 :     for (j = 0; j < setnum; j++) {
     532                 :         robj *setobj = dstkey ?
     533            8066 :             lookupKeyWrite(c->db,setkeys[j]) :
     534           16155 :             lookupKeyRead(c->db,setkeys[j]);
     535            8089 :         if (!setobj) {
     536               6 :             sets[j] = NULL;
     537               6 :             continue;
     538                 :         }
     539            8083 :         if (checkType(c,setobj,REDIS_SET)) {
     540               1 :             zfree(sets);
     541               1 :             return;
     542                 :         }
     543            8082 :         sets[j] = setobj;
     544                 :     }
     545                 : 
     546                 :     /* We need a temp set object to store our union. If the dstkey
     547                 :      * is not NULL (that is, we are inside an SUNIONSTORE operation) then
     548                 :      * this set object will be the resulting object to set into the target key*/
     549            4040 :     dstset = createIntsetObject();
     550                 : 
     551                 :     /* Iterate all the elements of all the sets, add every element a single
     552                 :      * time to the result set */
     553           12121 :     for (j = 0; j < setnum; j++) {
     554            8088 :         if (op == REDIS_OP_DIFF && j == 0 && !sets[j]) break; /* result set is empty */
     555            8088 :         if (!sets[j]) continue; /* non existing keys are like empty sets */
     556                 : 
     557            8082 :         si = setTypeInitIterator(sets[j]);
     558           30348 :         while((ele = setTypeNextObject(si)) != NULL) {
     559           14184 :             if (op == REDIS_OP_UNION || j == 0) {
     560           10511 :                 if (setTypeAdd(dstset,ele)) {
     561           10459 :                     cardinality++;
     562                 :                 }
     563            3673 :             } else if (op == REDIS_OP_DIFF) {
     564            3673 :                 if (setTypeRemove(dstset,ele)) {
     565            1187 :                     cardinality--;
     566                 :                 }
     567                 :             }
     568           14184 :             decrRefCount(ele);
     569                 :         }
     570            8082 :         setTypeReleaseIterator(si);
     571                 : 
     572                 :         /* Exit when result set is empty. */
     573            8082 :         if (op == REDIS_OP_DIFF && cardinality == 0) break;
     574                 :     }
     575                 : 
     576                 :     /* Output the content of the resulting set, if not in STORE mode */
     577            4040 :     if (!dstkey) {
     578               8 :         addReplyMultiBulkLen(c,cardinality);
     579               8 :         si = setTypeInitIterator(dstset);
     580            1616 :         while((ele = setTypeNextObject(si)) != NULL) {
     581            1600 :             addReplyBulk(c,ele);
     582            1600 :             decrRefCount(ele);
     583                 :         }
     584               8 :         setTypeReleaseIterator(si);
     585               8 :         decrRefCount(dstset);
     586                 :     } else {
     587                 :         /* If we have a target key where to store the resulting set
     588                 :          * create this key with the result set inside */
     589            4032 :         dbDelete(c->db,dstkey);
     590            4032 :         if (setTypeSize(dstset) > 0) {
     591            4024 :             dbAdd(c->db,dstkey,dstset);
     592            4024 :             addReplyLongLong(c,setTypeSize(dstset));
     593                 :         } else {
     594               8 :             decrRefCount(dstset);
     595               8 :             addReply(c,shared.czero);
     596                 :         }
     597            4032 :         signalModifiedKey(c->db,dstkey);
     598            4032 :         server.dirty++;
     599                 :     }
     600            4040 :     zfree(sets);
     601                 : }
     602                 : 
     603               5 : void sunionCommand(redisClient *c) {
     604               5 :     sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_UNION);
     605               5 : }
     606                 : 
     607            1975 : void sunionstoreCommand(redisClient *c) {
     608            1975 :     sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_UNION);
     609            1975 : }
     610                 : 
     611               4 : void sdiffCommand(redisClient *c) {
     612               4 :     sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_DIFF);
     613               4 : }
     614                 : 
     615            2057 : void sdiffstoreCommand(redisClient *c) {
     616            2057 :     sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_DIFF);
     617            2057 : }

Generated by: LCOV version 1.7