BITCOUNT and BITPOS with non-existing key and illegal arguments should return error, not 0 (#11734)
BITCOUNT and BITPOS with non-existing key will return 0 even the arguments are error, before this commit: ``` > flushall OK > bitcount s 0 (integer) 0 > bitpos s 0 0 1 hello (integer) 0 > set s 1 OK > bitcount s 0 (error) ERR syntax error > bitpos s 0 0 1 hello (error) ERR syntax error ``` The reason is that we judged non-existing before parameter checking and returned. This PR fixes it, and after this commit: ``` > flushall OK > bitcount s 0 (error) ERR syntax error > bitpos s 0 0 1 hello (error) ERR syntax error ``` Also BITPOS made the same fix as #12394, check for wrong argument, before checking for key. ``` > lpush mylist a b c (integer) 3 > bitpos mylist 1 a b (error) WRONGTYPE Operation against a key holding the wrong kind of value ```
This commit is contained in:
parent
45d3310694
commit
1407ac1f3e
58
src/bitops.c
58
src/bitops.c
@ -816,9 +816,9 @@ void bitcountCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Lookup, check for type, and return 0 for non existing keys. */
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,o,OBJ_STRING)) return;
|
||||
/* Lookup, check for type. */
|
||||
o = lookupKeyRead(c->db, c->argv[1]);
|
||||
if (checkType(c, o, OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
long long totlen = strlen;
|
||||
|
||||
@ -845,9 +845,9 @@ void bitcountCommand(client *c) {
|
||||
end >>= 3;
|
||||
}
|
||||
} else if (c->argc == 2) {
|
||||
/* Lookup, check for type, and return 0 for non existing keys. */
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,o,OBJ_STRING)) return;
|
||||
/* Lookup, check for type. */
|
||||
o = lookupKeyRead(c->db, c->argv[1]);
|
||||
if (checkType(c, o, OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
/* The whole string. */
|
||||
start = 0;
|
||||
@ -858,6 +858,12 @@ void bitcountCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Return 0 for non existing keys. */
|
||||
if (o == NULL) {
|
||||
addReply(c, shared.czero);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Precondition: end >= 0 && end < strlen, so the only condition where
|
||||
* zero can be returned is: start > end. */
|
||||
if (start > end) {
|
||||
@ -897,21 +903,8 @@ void bitposCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the key does not exist, from our point of view it is an infinite
|
||||
* array of 0 bits. If the user is looking for the first clear bit return 0,
|
||||
* If the user is looking for the first set bit, return -1. */
|
||||
if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
|
||||
addReplyLongLong(c, bit ? -1 : 0);
|
||||
return;
|
||||
}
|
||||
if (checkType(c,o,OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
|
||||
/* Parse start/end range if any. */
|
||||
if (c->argc == 4 || c->argc == 5 || c->argc == 6) {
|
||||
long long totlen = strlen;
|
||||
/* Make sure we will not overflow */
|
||||
serverAssert(totlen <= LLONG_MAX >> 3);
|
||||
if (getLongLongFromObjectOrReply(c,c->argv[3],&start,NULL) != C_OK)
|
||||
return;
|
||||
if (c->argc == 6) {
|
||||
@ -926,10 +919,22 @@ void bitposCommand(client *c) {
|
||||
if (getLongLongFromObjectOrReply(c,c->argv[4],&end,NULL) != C_OK)
|
||||
return;
|
||||
end_given = 1;
|
||||
} else {
|
||||
}
|
||||
|
||||
/* Lookup, check for type. */
|
||||
o = lookupKeyRead(c->db, c->argv[1]);
|
||||
if (checkType(c, o, OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o, &strlen, llbuf);
|
||||
|
||||
/* Make sure we will not overflow */
|
||||
long long totlen = strlen;
|
||||
serverAssert(totlen <= LLONG_MAX >> 3);
|
||||
|
||||
if (c->argc < 5) {
|
||||
if (isbit) end = (totlen<<3) + 7;
|
||||
else end = totlen-1;
|
||||
}
|
||||
|
||||
if (isbit) totlen <<= 3;
|
||||
/* Convert negative indexes */
|
||||
if (start < 0) start = totlen+start;
|
||||
@ -946,6 +951,11 @@ void bitposCommand(client *c) {
|
||||
end >>= 3;
|
||||
}
|
||||
} else if (c->argc == 3) {
|
||||
/* Lookup, check for type. */
|
||||
o = lookupKeyRead(c->db, c->argv[1]);
|
||||
if (checkType(c,o,OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
|
||||
/* The whole string. */
|
||||
start = 0;
|
||||
end = strlen-1;
|
||||
@ -955,6 +965,14 @@ void bitposCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the key does not exist, from our point of view it is an infinite
|
||||
* array of 0 bits. If the user is looking for the first clear bit return 0,
|
||||
* If the user is looking for the first set bit, return -1. */
|
||||
if (o == NULL) {
|
||||
addReplyLongLong(c, bit ? -1 : 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* For empty ranges (start > end) we return -1 as an empty range does
|
||||
* not contain a 0 nor a 1. */
|
||||
if (start > end) {
|
||||
|
@ -45,7 +45,19 @@ proc simulate_bit_op {op args} {
|
||||
}
|
||||
|
||||
start_server {tags {"bitops"}} {
|
||||
test {BITCOUNT against wrong type} {
|
||||
r del mylist
|
||||
r lpush mylist a b c
|
||||
assert_error "*WRONGTYPE*" {r bitcount mylist}
|
||||
assert_error "*WRONGTYPE*" {r bitcount mylist 0 100}
|
||||
|
||||
# with negative indexes where start > end
|
||||
assert_error "*WRONGTYPE*" {r bitcount mylist -6 -7}
|
||||
assert_error "*WRONGTYPE*" {r bitcount mylist -6 -15 bit}
|
||||
}
|
||||
|
||||
test {BITCOUNT returns 0 against non existing key} {
|
||||
r del no-key
|
||||
assert {[r bitcount no-key] == 0}
|
||||
assert {[r bitcount no-key 0 1000 bit] == 0}
|
||||
}
|
||||
@ -60,6 +72,11 @@ start_server {tags {"bitops"}} {
|
||||
r set str "xxxx"
|
||||
assert {[r bitcount str -6 -7] == 0}
|
||||
assert {[r bitcount str -6 -15 bit] == 0}
|
||||
|
||||
# against non existing key
|
||||
r del str
|
||||
assert {[r bitcount str -6 -7] == 0}
|
||||
assert {[r bitcount str -6 -15 bit] == 0}
|
||||
}
|
||||
|
||||
catch {unset num}
|
||||
@ -130,20 +147,32 @@ start_server {tags {"bitops"}} {
|
||||
assert_equal [r bitcount s 0 1000 bit] [count_bits $s]
|
||||
}
|
||||
|
||||
test {BITCOUNT syntax error #1} {
|
||||
catch {r bitcount s 0} e
|
||||
set e
|
||||
} {ERR *syntax*}
|
||||
test {BITCOUNT with illegal arguments} {
|
||||
# Used to return 0 for non-existing key instead of errors
|
||||
r del s
|
||||
assert_error {ERR *syntax*} {r bitcount s 0}
|
||||
assert_error {ERR *syntax*} {r bitcount s 0 1 hello}
|
||||
assert_error {ERR *syntax*} {r bitcount s 0 1 hello hello2}
|
||||
|
||||
test {BITCOUNT syntax error #2} {
|
||||
catch {r bitcount s 0 1 hello} e
|
||||
set e
|
||||
} {ERR *syntax*}
|
||||
r set s 1
|
||||
assert_error {ERR *syntax*} {r bitcount s 0}
|
||||
assert_error {ERR *syntax*} {r bitcount s 0 1 hello}
|
||||
assert_error {ERR *syntax*} {r bitcount s 0 1 hello hello2}
|
||||
}
|
||||
|
||||
test {BITCOUNT against non-integer value} {
|
||||
catch {r bitcount no-key a b} e
|
||||
set e
|
||||
} {ERR *not an integer*}
|
||||
# against existing key
|
||||
r set s 1
|
||||
assert_error {ERR *not an integer*} {r bitcount s a b}
|
||||
|
||||
# against non existing key
|
||||
r del s
|
||||
assert_error {ERR *not an integer*} {r bitcount s a b}
|
||||
|
||||
# against wrong type
|
||||
r lpush s a b c
|
||||
assert_error {ERR *not an integer*} {r bitcount s a b}
|
||||
}
|
||||
|
||||
test {BITCOUNT regression test for github issue #582} {
|
||||
r del foo
|
||||
@ -262,6 +291,41 @@ start_server {tags {"bitops"}} {
|
||||
r bitop or x{t} a{t} b{t}
|
||||
} {32}
|
||||
|
||||
test {BITPOS against wrong type} {
|
||||
r del mylist
|
||||
r lpush mylist a b c
|
||||
assert_error "*WRONGTYPE*" {r bitpos mylist 0}
|
||||
assert_error "*WRONGTYPE*" {r bitpos mylist 1 10 100}
|
||||
}
|
||||
|
||||
test {BITPOS will illegal arguments} {
|
||||
# Used to return 0 for non-existing key instead of errors
|
||||
r del s
|
||||
assert_error {ERR *syntax*} {r bitpos s 0 1 hello hello2}
|
||||
assert_error {ERR *syntax*} {r bitpos s 0 0 1 hello}
|
||||
|
||||
r set s 1
|
||||
assert_error {ERR *syntax*} {r bitpos s 0 1 hello hello2}
|
||||
assert_error {ERR *syntax*} {r bitpos s 0 0 1 hello}
|
||||
}
|
||||
|
||||
test {BITPOS against non-integer value} {
|
||||
# against existing key
|
||||
r set s 1
|
||||
assert_error {ERR *not an integer*} {r bitpos s a}
|
||||
assert_error {ERR *not an integer*} {r bitpos s 0 a b}
|
||||
|
||||
# against non existing key
|
||||
r del s
|
||||
assert_error {ERR *not an integer*} {r bitpos s b}
|
||||
assert_error {ERR *not an integer*} {r bitpos s 0 a b}
|
||||
|
||||
# against wrong type
|
||||
r lpush s a b c
|
||||
assert_error {ERR *not an integer*} {r bitpos s a}
|
||||
assert_error {ERR *not an integer*} {r bitpos s 1 a b}
|
||||
}
|
||||
|
||||
test {BITPOS bit=0 with empty key returns 0} {
|
||||
r del str
|
||||
assert {[r bitpos str 0] == 0}
|
||||
|
Loading…
x
Reference in New Issue
Block a user