summaryrefslogtreecommitdiff
path: root/src/t_set.c
diff options
context:
space:
mode:
authorBinbin <binloveplay1314@qq.com>2021-06-13 15:53:46 +0800
committerGitHub <noreply@github.com>2021-06-13 10:53:46 +0300
commitb8a5da80c49501773f8778aaf5cbf595cef615e4 (patch)
treeecae20211cbcc0fb5892380078758d235f192272 /src/t_set.c
parent5517f3624d900882eee39b06c494223087326417 (diff)
downloadredis-b8a5da80c49501773f8778aaf5cbf595cef615e4.tar.gz
Fix accidental deletion of sinterstore command when we meet wrong type error. (#9032)
SINTERSTORE would have deleted the dest key right away, even when later on it is bound to fail on an (WRONGTYPE) error. With this change it first picks up all the input keys, and only later delete the dest key if one is empty. Also add more tests for some commands. Mainly focus on - `wrong type error`: expand test case (base on sinter bug) in non-store variant add tests for store variant (although it exists in non-store variant, i think it would be better to have same tests) - the dstkey result when we meet `non-exist key (empty set)` in *store sdiff: - improve test case about wrong type error (the one we found in sinter, although it is safe in sdiff) - add test about using non-exist key (treat it like an empty set) sdiffstore: - according to sdiff test case, also add some tests about `wrong type error` and `non-exist key` - the different is that in sdiffstore, we will consider the `dstkey` result sunion/sunionstore add more tests (same as above) sinter/sinterstore also same as above ...
Diffstat (limited to 'src/t_set.c')
-rw-r--r--src/t_set.c42
1 files changed, 29 insertions, 13 deletions
diff --git a/src/t_set.c b/src/t_set.c
index bf250baa2..d0c54848e 100644
--- a/src/t_set.c
+++ b/src/t_set.c
@@ -858,25 +858,17 @@ void sinterGenericCommand(client *c, robj **setkeys,
int64_t intobj;
void *replylen = NULL;
unsigned long j, cardinality = 0;
- int encoding;
+ int encoding, empty = 0;
for (j = 0; j < setnum; j++) {
robj *setobj = dstkey ?
lookupKeyWrite(c->db,setkeys[j]) :
lookupKeyRead(c->db,setkeys[j]);
if (!setobj) {
- zfree(sets);
- if (dstkey) {
- if (dbDelete(c->db,dstkey)) {
- signalModifiedKey(c,c->db,dstkey);
- notifyKeyspaceEvent(NOTIFY_GENERIC,"del",dstkey,c->db->id);
- server.dirty++;
- }
- addReply(c,shared.czero);
- } else {
- addReply(c,shared.emptyset[c->resp]);
- }
- return;
+ /* A NULL is considered an empty set */
+ empty += 1;
+ sets[j] = NULL;
+ continue;
}
if (checkType(c,setobj,OBJ_SET)) {
zfree(sets);
@@ -884,6 +876,24 @@ void sinterGenericCommand(client *c, robj **setkeys,
}
sets[j] = setobj;
}
+
+ /* Set intersection with an empty set always results in an empty set.
+ * Return ASAP if there is an empty set. */
+ if (empty > 0) {
+ zfree(sets);
+ if (dstkey) {
+ if (dbDelete(c->db,dstkey)) {
+ signalModifiedKey(c,c->db,dstkey);
+ notifyKeyspaceEvent(NOTIFY_GENERIC,"del",dstkey,c->db->id);
+ server.dirty++;
+ }
+ addReply(c,shared.czero);
+ } else {
+ addReply(c,shared.emptyset[c->resp]);
+ }
+ return;
+ }
+
/* Sort sets from the smallest to largest, this will improve our
* algorithm's performance */
qsort(sets,setnum,sizeof(robj*),qsortCompareSetsByCardinality);
@@ -977,10 +987,12 @@ void sinterGenericCommand(client *c, robj **setkeys,
zfree(sets);
}
+/* SINTER key [key ...] */
void sinterCommand(client *c) {
sinterGenericCommand(c,c->argv+1,c->argc-1,NULL);
}
+/* SINTERSTORE destination key [key ...] */
void sinterstoreCommand(client *c) {
sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);
}
@@ -1150,18 +1162,22 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
zfree(sets);
}
+/* SUNION key [key ...] */
void sunionCommand(client *c) {
sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,SET_OP_UNION);
}
+/* SUNIONSTORE destination key [key ...] */
void sunionstoreCommand(client *c) {
sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],SET_OP_UNION);
}
+/* SDIFF key [key ...] */
void sdiffCommand(client *c) {
sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,SET_OP_DIFF);
}
+/* SDIFFSTORE destination key [key ...] */
void sdiffstoreCommand(client *c) {
sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],SET_OP_DIFF);
}