3 /*-----------------------------------------------------------------------------
5 *----------------------------------------------------------------------------*/
7 void saddCommand(redisClient
*c
) {
10 set
= lookupKeyWrite(c
->db
,c
->argv
[1]);
12 set
= createSetObject();
13 dbAdd(c
->db
,c
->argv
[1],set
);
15 if (set
->type
!= REDIS_SET
) {
16 addReply(c
,shared
.wrongtypeerr
);
20 if (dictAdd(set
->ptr
,c
->argv
[2],NULL
) == DICT_OK
) {
21 incrRefCount(c
->argv
[2]);
23 addReply(c
,shared
.cone
);
25 addReply(c
,shared
.czero
);
29 void sremCommand(redisClient
*c
) {
32 if ((set
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
33 checkType(c
,set
,REDIS_SET
)) return;
35 if (dictDelete(set
->ptr
,c
->argv
[2]) == DICT_OK
) {
37 if (htNeedsResize(set
->ptr
)) dictResize(set
->ptr
);
38 if (dictSize((dict
*)set
->ptr
) == 0) dbDelete(c
->db
,c
->argv
[1]);
39 addReply(c
,shared
.cone
);
41 addReply(c
,shared
.czero
);
45 void smoveCommand(redisClient
*c
) {
46 robj
*srcset
, *dstset
;
48 srcset
= lookupKeyWrite(c
->db
,c
->argv
[1]);
49 dstset
= lookupKeyWrite(c
->db
,c
->argv
[2]);
51 /* If the source key does not exist return 0, if it's of the wrong type
53 if (srcset
== NULL
|| srcset
->type
!= REDIS_SET
) {
54 addReply(c
, srcset
? shared
.wrongtypeerr
: shared
.czero
);
57 /* Error if the destination key is not a set as well */
58 if (dstset
&& dstset
->type
!= REDIS_SET
) {
59 addReply(c
,shared
.wrongtypeerr
);
62 /* Remove the element from the source set */
63 if (dictDelete(srcset
->ptr
,c
->argv
[3]) == DICT_ERR
) {
64 /* Key not found in the src set! return zero */
65 addReply(c
,shared
.czero
);
68 if (dictSize((dict
*)srcset
->ptr
) == 0 && srcset
!= dstset
)
69 dbDelete(c
->db
,c
->argv
[1]);
71 /* Add the element to the destination set */
73 dstset
= createSetObject();
74 dbAdd(c
->db
,c
->argv
[2],dstset
);
76 if (dictAdd(dstset
->ptr
,c
->argv
[3],NULL
) == DICT_OK
)
77 incrRefCount(c
->argv
[3]);
78 addReply(c
,shared
.cone
);
81 void sismemberCommand(redisClient
*c
) {
84 if ((set
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
85 checkType(c
,set
,REDIS_SET
)) return;
87 if (dictFind(set
->ptr
,c
->argv
[2]))
88 addReply(c
,shared
.cone
);
90 addReply(c
,shared
.czero
);
93 void scardCommand(redisClient
*c
) {
97 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
98 checkType(c
,o
,REDIS_SET
)) return;
101 addReplyUlong(c
,dictSize(s
));
104 void spopCommand(redisClient
*c
) {
108 if ((set
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
109 checkType(c
,set
,REDIS_SET
)) return;
111 de
= dictGetRandomKey(set
->ptr
);
113 addReply(c
,shared
.nullbulk
);
115 robj
*ele
= dictGetEntryKey(de
);
118 dictDelete(set
->ptr
,ele
);
119 if (htNeedsResize(set
->ptr
)) dictResize(set
->ptr
);
120 if (dictSize((dict
*)set
->ptr
) == 0) dbDelete(c
->db
,c
->argv
[1]);
125 void srandmemberCommand(redisClient
*c
) {
129 if ((set
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
130 checkType(c
,set
,REDIS_SET
)) return;
132 de
= dictGetRandomKey(set
->ptr
);
134 addReply(c
,shared
.nullbulk
);
136 robj
*ele
= dictGetEntryKey(de
);
142 int qsortCompareSetsByCardinality(const void *s1
, const void *s2
) {
143 dict
**d1
= (void*) s1
, **d2
= (void*) s2
;
145 return dictSize(*d1
)-dictSize(*d2
);
148 void sinterGenericCommand(redisClient
*c
, robj
**setskeys
, unsigned long setsnum
, robj
*dstkey
) {
149 dict
**dv
= zmalloc(sizeof(dict
*)*setsnum
);
152 robj
*lenobj
= NULL
, *dstset
= NULL
;
153 unsigned long j
, cardinality
= 0;
155 for (j
= 0; j
< setsnum
; j
++) {
159 lookupKeyWrite(c
->db
,setskeys
[j
]) :
160 lookupKeyRead(c
->db
,setskeys
[j
]);
164 if (dbDelete(c
->db
,dstkey
))
166 addReply(c
,shared
.czero
);
168 addReply(c
,shared
.emptymultibulk
);
172 if (setobj
->type
!= REDIS_SET
) {
174 addReply(c
,shared
.wrongtypeerr
);
179 /* Sort sets from the smallest to largest, this will improve our
180 * algorithm's performace */
181 qsort(dv
,setsnum
,sizeof(dict
*),qsortCompareSetsByCardinality
);
183 /* The first thing we should output is the total number of elements...
184 * since this is a multi-bulk write, but at this stage we don't know
185 * the intersection set size, so we use a trick, append an empty object
186 * to the output list and save the pointer to later modify it with the
189 lenobj
= createObject(REDIS_STRING
,NULL
);
191 decrRefCount(lenobj
);
193 /* If we have a target key where to store the resulting set
194 * create this key with an empty set inside */
195 dstset
= createSetObject();
198 /* Iterate all the elements of the first (smallest) set, and test
199 * the element against all the other sets, if at least one set does
200 * not include the element it is discarded */
201 di
= dictGetIterator(dv
[0]);
203 while((de
= dictNext(di
)) != NULL
) {
206 for (j
= 1; j
< setsnum
; j
++)
207 if (dictFind(dv
[j
],dictGetEntryKey(de
)) == NULL
) break;
209 continue; /* at least one set does not contain the member */
210 ele
= dictGetEntryKey(de
);
215 dictAdd(dstset
->ptr
,ele
,NULL
);
219 dictReleaseIterator(di
);
222 /* Store the resulting set into the target, if the intersection
223 * is not an empty set. */
224 dbDelete(c
->db
,dstkey
);
225 if (dictSize((dict
*)dstset
->ptr
) > 0) {
226 dbAdd(c
->db
,dstkey
,dstset
);
227 addReplyLongLong(c
,dictSize((dict
*)dstset
->ptr
));
229 decrRefCount(dstset
);
230 addReply(c
,shared
.czero
);
234 lenobj
->ptr
= sdscatprintf(sdsempty(),"*%lu\r\n",cardinality
);
239 void sinterCommand(redisClient
*c
) {
240 sinterGenericCommand(c
,c
->argv
+1,c
->argc
-1,NULL
);
243 void sinterstoreCommand(redisClient
*c
) {
244 sinterGenericCommand(c
,c
->argv
+2,c
->argc
-2,c
->argv
[1]);
247 void sunionDiffGenericCommand(redisClient
*c
, robj
**setskeys
, int setsnum
, robj
*dstkey
, int op
) {
248 dict
**dv
= zmalloc(sizeof(dict
*)*setsnum
);
252 int j
, cardinality
= 0;
254 for (j
= 0; j
< setsnum
; j
++) {
258 lookupKeyWrite(c
->db
,setskeys
[j
]) :
259 lookupKeyRead(c
->db
,setskeys
[j
]);
264 if (setobj
->type
!= REDIS_SET
) {
266 addReply(c
,shared
.wrongtypeerr
);
272 /* We need a temp set object to store our union. If the dstkey
273 * is not NULL (that is, we are inside an SUNIONSTORE operation) then
274 * this set object will be the resulting object to set into the target key*/
275 dstset
= createSetObject();
277 /* Iterate all the elements of all the sets, add every element a single
278 * time to the result set */
279 for (j
= 0; j
< setsnum
; j
++) {
280 if (op
== REDIS_OP_DIFF
&& j
== 0 && !dv
[j
]) break; /* result set is empty */
281 if (!dv
[j
]) continue; /* non existing keys are like empty sets */
283 di
= dictGetIterator(dv
[j
]);
285 while((de
= dictNext(di
)) != NULL
) {
288 /* dictAdd will not add the same element multiple times */
289 ele
= dictGetEntryKey(de
);
290 if (op
== REDIS_OP_UNION
|| j
== 0) {
291 if (dictAdd(dstset
->ptr
,ele
,NULL
) == DICT_OK
) {
295 } else if (op
== REDIS_OP_DIFF
) {
296 if (dictDelete(dstset
->ptr
,ele
) == DICT_OK
) {
301 dictReleaseIterator(di
);
303 /* result set is empty? Exit asap. */
304 if (op
== REDIS_OP_DIFF
&& cardinality
== 0) break;
307 /* Output the content of the resulting set, if not in STORE mode */
309 addReplySds(c
,sdscatprintf(sdsempty(),"*%d\r\n",cardinality
));
310 di
= dictGetIterator(dstset
->ptr
);
311 while((de
= dictNext(di
)) != NULL
) {
314 ele
= dictGetEntryKey(de
);
317 dictReleaseIterator(di
);
318 decrRefCount(dstset
);
320 /* If we have a target key where to store the resulting set
321 * create this key with the result set inside */
322 dbDelete(c
->db
,dstkey
);
323 if (dictSize((dict
*)dstset
->ptr
) > 0) {
324 dbAdd(c
->db
,dstkey
,dstset
);
325 addReplyLongLong(c
,dictSize((dict
*)dstset
->ptr
));
327 decrRefCount(dstset
);
328 addReply(c
,shared
.czero
);
335 void sunionCommand(redisClient
*c
) {
336 sunionDiffGenericCommand(c
,c
->argv
+1,c
->argc
-1,NULL
,REDIS_OP_UNION
);
339 void sunionstoreCommand(redisClient
*c
) {
340 sunionDiffGenericCommand(c
,c
->argv
+2,c
->argc
-2,c
->argv
[1],REDIS_OP_UNION
);
343 void sdiffCommand(redisClient
*c
) {
344 sunionDiffGenericCommand(c
,c
->argv
+1,c
->argc
-1,NULL
,REDIS_OP_DIFF
);
347 void sdiffstoreCommand(redisClient
*c
) {
348 sunionDiffGenericCommand(c
,c
->argv
+2,c
->argc
-2,c
->argv
[1],REDIS_OP_DIFF
);