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]);
22 touchWatchedKey(c
->db
,c
->argv
[1]);
24 addReply(c
,shared
.cone
);
26 addReply(c
,shared
.czero
);
30 void sremCommand(redisClient
*c
) {
33 if ((set
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
34 checkType(c
,set
,REDIS_SET
)) return;
36 if (dictDelete(set
->ptr
,c
->argv
[2]) == DICT_OK
) {
38 touchWatchedKey(c
->db
,c
->argv
[1]);
39 if (htNeedsResize(set
->ptr
)) dictResize(set
->ptr
);
40 if (dictSize((dict
*)set
->ptr
) == 0) dbDelete(c
->db
,c
->argv
[1]);
41 addReply(c
,shared
.cone
);
43 addReply(c
,shared
.czero
);
47 void smoveCommand(redisClient
*c
) {
48 robj
*srcset
, *dstset
;
50 srcset
= lookupKeyWrite(c
->db
,c
->argv
[1]);
51 dstset
= lookupKeyWrite(c
->db
,c
->argv
[2]);
53 /* If the source key does not exist return 0, if it's of the wrong type
55 if (srcset
== NULL
|| srcset
->type
!= REDIS_SET
) {
56 addReply(c
, srcset
? shared
.wrongtypeerr
: shared
.czero
);
59 /* Error if the destination key is not a set as well */
60 if (dstset
&& dstset
->type
!= REDIS_SET
) {
61 addReply(c
,shared
.wrongtypeerr
);
64 /* Remove the element from the source set */
65 if (dictDelete(srcset
->ptr
,c
->argv
[3]) == DICT_ERR
) {
66 /* Key not found in the src set! return zero */
67 addReply(c
,shared
.czero
);
70 if (dictSize((dict
*)srcset
->ptr
) == 0 && srcset
!= dstset
)
71 dbDelete(c
->db
,c
->argv
[1]);
72 touchWatchedKey(c
->db
,c
->argv
[1]);
73 touchWatchedKey(c
->db
,c
->argv
[2]);
75 /* Add the element to the destination set */
77 dstset
= createSetObject();
78 dbAdd(c
->db
,c
->argv
[2],dstset
);
80 if (dictAdd(dstset
->ptr
,c
->argv
[3],NULL
) == DICT_OK
)
81 incrRefCount(c
->argv
[3]);
82 addReply(c
,shared
.cone
);
85 void sismemberCommand(redisClient
*c
) {
88 if ((set
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
89 checkType(c
,set
,REDIS_SET
)) return;
91 if (dictFind(set
->ptr
,c
->argv
[2]))
92 addReply(c
,shared
.cone
);
94 addReply(c
,shared
.czero
);
97 void scardCommand(redisClient
*c
) {
101 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
102 checkType(c
,o
,REDIS_SET
)) return;
105 addReplyUlong(c
,dictSize(s
));
108 void spopCommand(redisClient
*c
) {
112 if ((set
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
113 checkType(c
,set
,REDIS_SET
)) return;
115 de
= dictGetRandomKey(set
->ptr
);
117 addReply(c
,shared
.nullbulk
);
119 robj
*ele
= dictGetEntryKey(de
);
122 dictDelete(set
->ptr
,ele
);
123 if (htNeedsResize(set
->ptr
)) dictResize(set
->ptr
);
124 if (dictSize((dict
*)set
->ptr
) == 0) dbDelete(c
->db
,c
->argv
[1]);
125 touchWatchedKey(c
->db
,c
->argv
[1]);
130 void srandmemberCommand(redisClient
*c
) {
134 if ((set
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
135 checkType(c
,set
,REDIS_SET
)) return;
137 de
= dictGetRandomKey(set
->ptr
);
139 addReply(c
,shared
.nullbulk
);
141 robj
*ele
= dictGetEntryKey(de
);
147 int qsortCompareSetsByCardinality(const void *s1
, const void *s2
) {
148 dict
**d1
= (void*) s1
, **d2
= (void*) s2
;
150 return dictSize(*d1
)-dictSize(*d2
);
153 void sinterGenericCommand(redisClient
*c
, robj
**setskeys
, unsigned long setsnum
, robj
*dstkey
) {
154 dict
**dv
= zmalloc(sizeof(dict
*)*setsnum
);
157 robj
*lenobj
= NULL
, *dstset
= NULL
;
158 unsigned long j
, cardinality
= 0;
160 for (j
= 0; j
< setsnum
; j
++) {
164 lookupKeyWrite(c
->db
,setskeys
[j
]) :
165 lookupKeyRead(c
->db
,setskeys
[j
]);
169 if (dbDelete(c
->db
,dstkey
)) {
170 touchWatchedKey(c
->db
,dstkey
);
173 addReply(c
,shared
.czero
);
175 addReply(c
,shared
.emptymultibulk
);
179 if (setobj
->type
!= REDIS_SET
) {
181 addReply(c
,shared
.wrongtypeerr
);
186 /* Sort sets from the smallest to largest, this will improve our
187 * algorithm's performace */
188 qsort(dv
,setsnum
,sizeof(dict
*),qsortCompareSetsByCardinality
);
190 /* The first thing we should output is the total number of elements...
191 * since this is a multi-bulk write, but at this stage we don't know
192 * the intersection set size, so we use a trick, append an empty object
193 * to the output list and save the pointer to later modify it with the
196 lenobj
= createObject(REDIS_STRING
,NULL
);
198 decrRefCount(lenobj
);
200 /* If we have a target key where to store the resulting set
201 * create this key with an empty set inside */
202 dstset
= createSetObject();
205 /* Iterate all the elements of the first (smallest) set, and test
206 * the element against all the other sets, if at least one set does
207 * not include the element it is discarded */
208 di
= dictGetIterator(dv
[0]);
210 while((de
= dictNext(di
)) != NULL
) {
213 for (j
= 1; j
< setsnum
; j
++)
214 if (dictFind(dv
[j
],dictGetEntryKey(de
)) == NULL
) break;
216 continue; /* at least one set does not contain the member */
217 ele
= dictGetEntryKey(de
);
222 dictAdd(dstset
->ptr
,ele
,NULL
);
226 dictReleaseIterator(di
);
229 /* Store the resulting set into the target, if the intersection
230 * is not an empty set. */
231 dbDelete(c
->db
,dstkey
);
232 if (dictSize((dict
*)dstset
->ptr
) > 0) {
233 dbAdd(c
->db
,dstkey
,dstset
);
234 addReplyLongLong(c
,dictSize((dict
*)dstset
->ptr
));
236 decrRefCount(dstset
);
237 addReply(c
,shared
.czero
);
239 touchWatchedKey(c
->db
,dstkey
);
242 lenobj
->ptr
= sdscatprintf(sdsempty(),"*%lu\r\n",cardinality
);
247 void sinterCommand(redisClient
*c
) {
248 sinterGenericCommand(c
,c
->argv
+1,c
->argc
-1,NULL
);
251 void sinterstoreCommand(redisClient
*c
) {
252 sinterGenericCommand(c
,c
->argv
+2,c
->argc
-2,c
->argv
[1]);
255 void sunionDiffGenericCommand(redisClient
*c
, robj
**setskeys
, int setsnum
, robj
*dstkey
, int op
) {
256 dict
**dv
= zmalloc(sizeof(dict
*)*setsnum
);
260 int j
, cardinality
= 0;
262 for (j
= 0; j
< setsnum
; j
++) {
266 lookupKeyWrite(c
->db
,setskeys
[j
]) :
267 lookupKeyRead(c
->db
,setskeys
[j
]);
272 if (setobj
->type
!= REDIS_SET
) {
274 addReply(c
,shared
.wrongtypeerr
);
280 /* We need a temp set object to store our union. If the dstkey
281 * is not NULL (that is, we are inside an SUNIONSTORE operation) then
282 * this set object will be the resulting object to set into the target key*/
283 dstset
= createSetObject();
285 /* Iterate all the elements of all the sets, add every element a single
286 * time to the result set */
287 for (j
= 0; j
< setsnum
; j
++) {
288 if (op
== REDIS_OP_DIFF
&& j
== 0 && !dv
[j
]) break; /* result set is empty */
289 if (!dv
[j
]) continue; /* non existing keys are like empty sets */
291 di
= dictGetIterator(dv
[j
]);
293 while((de
= dictNext(di
)) != NULL
) {
296 /* dictAdd will not add the same element multiple times */
297 ele
= dictGetEntryKey(de
);
298 if (op
== REDIS_OP_UNION
|| j
== 0) {
299 if (dictAdd(dstset
->ptr
,ele
,NULL
) == DICT_OK
) {
303 } else if (op
== REDIS_OP_DIFF
) {
304 if (dictDelete(dstset
->ptr
,ele
) == DICT_OK
) {
309 dictReleaseIterator(di
);
311 /* result set is empty? Exit asap. */
312 if (op
== REDIS_OP_DIFF
&& cardinality
== 0) break;
315 /* Output the content of the resulting set, if not in STORE mode */
317 addReplySds(c
,sdscatprintf(sdsempty(),"*%d\r\n",cardinality
));
318 di
= dictGetIterator(dstset
->ptr
);
319 while((de
= dictNext(di
)) != NULL
) {
322 ele
= dictGetEntryKey(de
);
325 dictReleaseIterator(di
);
326 decrRefCount(dstset
);
328 /* If we have a target key where to store the resulting set
329 * create this key with the result set inside */
330 dbDelete(c
->db
,dstkey
);
331 if (dictSize((dict
*)dstset
->ptr
) > 0) {
332 dbAdd(c
->db
,dstkey
,dstset
);
333 addReplyLongLong(c
,dictSize((dict
*)dstset
->ptr
));
335 decrRefCount(dstset
);
336 addReply(c
,shared
.czero
);
338 touchWatchedKey(c
->db
,dstkey
);
344 void sunionCommand(redisClient
*c
) {
345 sunionDiffGenericCommand(c
,c
->argv
+1,c
->argc
-1,NULL
,REDIS_OP_UNION
);
348 void sunionstoreCommand(redisClient
*c
) {
349 sunionDiffGenericCommand(c
,c
->argv
+2,c
->argc
-2,c
->argv
[1],REDIS_OP_UNION
);
352 void sdiffCommand(redisClient
*c
) {
353 sunionDiffGenericCommand(c
,c
->argv
+1,c
->argc
-1,NULL
,REDIS_OP_DIFF
);
356 void sdiffstoreCommand(redisClient
*c
) {
357 sunionDiffGenericCommand(c
,c
->argv
+2,c
->argc
-2,c
->argv
[1],REDIS_OP_DIFF
);