fix!: fix BITPOS command responses (#3893) (#3910)

BITPOS returns 0 for non-existent keys according to Redis's
implmentation.

BITPOS allows only 0 and 1 as the bit mode argument.

Signed-off-by: Denis K <kalantaevskii@gmail.com>
This commit is contained in:
Diskein 2024-10-12 09:55:01 +02:00 committed by GitHub
parent 5d2c308c99
commit ba57145c53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 5 deletions

View file

@ -548,6 +548,8 @@ void BitPos(CmdArgList args, ConnectionContext* cntx) {
if (!absl::SimpleAtoi(ArgS(args, 1), &value)) {
return cntx->SendError(kInvalidIntErr);
} else if (value != 0 && value != 1) {
return cntx->SendError("The bit argument must be 1 or 0");
}
if (args.size() >= 3) {
@ -1340,11 +1342,15 @@ OpResult<int64_t> FindFirstBitWithValue(const OpArgs& op_args, std::string_view
int64_t start, int64_t end, bool as_bit) {
OpResult<std::string> value = ReadValue(op_args.db_cntx, key, op_args.shard);
std::string_view value_str;
if (value) { // non-existent keys are treated as empty strings, per Redis
value_str = value.value();
// non-existent keys are handled exactly as in Redis's implementation,
// even though it contradicts its docs:
// If a clear bit isn't found in the specified range, the function returns -1
// as the user specified a clear range and there are no 0 bits in that range
if (!value) {
return bit_value ? -1 : 0;
}
std::string_view value_str = value.value();
int64_t size = value_str.size();
if (as_bit) {
size *= OFFSET_FACTOR;

View file

@ -555,8 +555,15 @@ TEST_F(BitOpsFamilyTest, BitPos) {
EXPECT_EQ(-1, CheckedInt({"bitpos", "empty", "0"}));
EXPECT_EQ(-1, CheckedInt({"bitpos", "empty", "0", "1"}));
// Non-existent key should be treated like an empty string.
EXPECT_EQ(-1, CheckedInt({"bitpos", "d", "0"}));
// Non-existent key should be treated like padded with zeros string.
EXPECT_EQ(-1, CheckedInt({"bitpos", "d", "1"}));
EXPECT_EQ(0, CheckedInt({"bitpos", "d", "0"}));
// Make sure we accept only 0 and 1 for the bit mode arguement.
const auto argument_must_be_0_or_1_error = ErrArg("ERR The bit argument must be 1 or 0");
ASSERT_THAT(Run({"bitpos", "d", "2"}), argument_must_be_0_or_1_error);
ASSERT_THAT(Run({"bitpos", "d", "42"}), argument_must_be_0_or_1_error);
ASSERT_THAT(Run({"bitpos", "d", "-1"}), argument_must_be_0_or_1_error);
}
TEST_F(BitOpsFamilyTest, BitFieldParsing) {