diff --git a/src/base_cmd.h b/src/base_cmd.h index 1c9698bdc..169a6f3ed 100644 --- a/src/base_cmd.h +++ b/src/base_cmd.h @@ -31,6 +31,7 @@ const std::string kCmdNamePExpireat = "pexpireat"; const std::string kCmdNamePersist = "persist"; const std::string kCmdNameKeys = "keys"; +const std::string kCmdNamePttl = "pttl"; // string cmd const std::string kCmdNameSet = "set"; const std::string kCmdNameGet = "get"; @@ -120,8 +121,14 @@ const std::string kCmdNameLLen = "llen"; const std::string kCmdNameZAdd = "zadd"; const std::string kCmdNameZRevrange = "zrevrange"; const std::string kCmdNameZRangebyscore = "zrangebyscore"; -const std::string kCmdNameZRevRangeByScore = "zrevrangebyscore"; +const std::string kCmdNameZRemrangebyscore = "zremrangebyscore"; +const std::string kCmdNameZRemrangebyrank = "zremrangebyrank"; +const std::string kCmdNameZRevrangebyscore = "zrevrangebyscore"; const std::string kCmdNameZCard = "zcard"; +const std::string kCmdNameZScore = "zscore"; +const std::string kCmdNameZRange = "zrange"; +const std::string kCmdNameZRangebylex = "zrangebylex"; +const std::string kCmdNameZRevrangebylex = "zrevrangebylex"; enum CmdFlags { kCmdFlagsWrite = (1 << 0), // May modify the dataset diff --git a/src/cmd_keys.cc b/src/cmd_keys.cc index 8499e247e..d00fc37fb 100644 --- a/src/cmd_keys.cc +++ b/src/cmd_keys.cc @@ -167,4 +167,59 @@ void KeysCmd::DoCmd(PClient* client) { } } +PttlCmd::PttlCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsReadonly, kAclCategoryRead | kAclCategoryKeyspace) {} + +bool PttlCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +// like Blackwidow , Floyd still possible has same key in different data structure +void PttlCmd::DoCmd(PClient* client) { + std::map type_status; + auto type_timestamp = PSTORE.GetBackend(client->GetCurrentDB())->GetStorage()->TTL(client->Key(), &type_status); + for (const auto& item : type_timestamp) { + // mean operation exception errors happen in database + if (item.second == -3) { + client->SetRes(CmdRes::kErrOther, "ttl internal error"); + return; + } + } + if (type_timestamp[storage::kStrings] != -2) { + if (type_timestamp[storage::kStrings] == -1) { + client->AppendInteger(-1); + } else { + client->AppendInteger(type_timestamp[storage::kStrings] * 1000); + } + } else if (type_timestamp[storage::kHashes] != -2) { + if (type_timestamp[storage::kHashes] == -1) { + client->AppendInteger(-1); + } else { + client->AppendInteger(type_timestamp[storage::kHashes] * 1000); + } + } else if (type_timestamp[storage::kLists] != -2) { + if (type_timestamp[storage::kLists] == -1) { + client->AppendInteger(-1); + } else { + client->AppendInteger(type_timestamp[storage::kLists] * 1000); + } + } else if (type_timestamp[storage::kSets] != -2) { + if (type_timestamp[storage::kSets] == -1) { + client->AppendInteger(-1); + } else { + client->AppendInteger(type_timestamp[storage::kSets] * 1000); + } + } else if (type_timestamp[storage::kZSets] != -2) { + if (type_timestamp[storage::kZSets] == -1) { + client->AppendInteger(-1); + } else { + client->AppendInteger(type_timestamp[storage::kZSets] * 1000); + } + } else { + // this key not exist + client->AppendInteger(-2); + } +} + } // namespace pikiwidb diff --git a/src/cmd_keys.h b/src/cmd_keys.h index c78453cee..252c3b3cc 100644 --- a/src/cmd_keys.h +++ b/src/cmd_keys.h @@ -87,4 +87,16 @@ class KeysCmd : public BaseCmd { private: void DoCmd(PClient* client) override; }; + +class PttlCmd : public BaseCmd { + public: + PttlCmd(const std::string& name, int16_t arity); + + protected: + bool DoInitial(PClient* client) override; + + private: + void DoCmd(PClient* client) override; +}; + } // namespace pikiwidb diff --git a/src/cmd_table_manager.cc b/src/cmd_table_manager.cc index 4eb8e4524..e0dadbf49 100644 --- a/src/cmd_table_manager.cc +++ b/src/cmd_table_manager.cc @@ -49,6 +49,7 @@ void CmdTableManager::InitCmdTable() { ADD_COMMAND(PExpire, 3); ADD_COMMAND(Expireat, 3); ADD_COMMAND(PExpireat, 3); + ADD_COMMAND(Pttl, 2); ADD_COMMAND(Persist, 2); ADD_COMMAND(Keys, 2); @@ -127,8 +128,14 @@ void CmdTableManager::InitCmdTable() { ADD_COMMAND(ZAdd, -4); ADD_COMMAND(ZRevrange, -4); ADD_COMMAND(ZRangebyscore, -4); - ADD_COMMAND(ZRevRangeByScore, -4); + ADD_COMMAND(ZRemrangebyscore, 4); + ADD_COMMAND(ZRemrangebyrank, 4); + ADD_COMMAND(ZRevrangebyscore, -4); ADD_COMMAND(ZCard, 2); + ADD_COMMAND(ZScore, 3); + ADD_COMMAND(ZRange, -4); + ADD_COMMAND(ZRangebylex, -3); + ADD_COMMAND(ZRevrangebylex, -3); } std::pair CmdTableManager::GetCommand(const std::string& cmdName, PClient* client) { diff --git a/src/cmd_zset.cc b/src/cmd_zset.cc index 1ff8da4fc..44bcfd956 100644 --- a/src/cmd_zset.cc +++ b/src/cmd_zset.cc @@ -48,6 +48,40 @@ int32_t DoScoreStrRange(std::string begin_score, std::string end_score, bool* le return 0; } +static int32_t DoMemberRange(const std::string& raw_min_member, const std::string& raw_max_member, bool* left_close, + bool* right_close, std::string* min_member, std::string* max_member) { + if (raw_min_member == "-") { + *min_member = "-"; + } else if (raw_min_member == "+") { + *min_member = "+"; + } else { + if (!raw_min_member.empty() && raw_min_member.at(0) == '(') { + *left_close = false; + } else if (!raw_min_member.empty() && raw_min_member.at(0) == '[') { + *left_close = true; + } else { + return -1; + } + min_member->assign(raw_min_member.begin() + 1, raw_min_member.end()); + } + + if (raw_max_member == "+") { + *max_member = "+"; + } else if (raw_max_member == "-") { + *max_member = "-"; + } else { + if (!raw_max_member.empty() && raw_max_member.at(0) == '(') { + *right_close = false; + } else if (!raw_max_member.empty() && raw_max_member.at(0) == '[') { + *right_close = true; + } else { + return -1; + } + max_member->assign(raw_max_member.begin() + 1, raw_max_member.end()); + } + return 0; +} + ZAddCmd::ZAddCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} @@ -251,15 +285,209 @@ void ZCardCmd::DoCmd(PClient* client) { client->AppendInteger(reply_Num); } -ZRevRangeByScoreCmd::ZRevRangeByScoreCmd(const std::string& name, int16_t arity) +ZRemrangebyrankCmd::ZRemrangebyrankCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategoryString) {} + +bool ZRemrangebyrankCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZRemrangebyrankCmd::DoCmd(PClient* client) { + int32_t ret = 0; + int32_t start = 0; + int32_t end = 0; + + if (pstd::String2int(client->argv_[2], &start) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + if (pstd::String2int(client->argv_[3], &end) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + + storage::Status s; + s = PSTORE.GetBackend(client->GetCurrentDB())->GetStorage()->ZRemrangebyrank(client->Key(), start, end, &ret); + if (s.ok() || s.IsNotFound()) { + client->AppendInteger(ret); + } else { + client->SetRes(CmdRes::kErrOther, s.ToString()); + } +} + +ZRangeCmd::ZRangeCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsReadonly, kAclCategoryRead | kAclCategorySortedSet) {} + +bool ZRangeCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZRangeCmd::DoCmd(PClient* client) { + double start = 0; + double stop = 0; + int64_t count = -1; + int64_t offset = 0; + bool with_scores = false; + bool by_score = false; + bool by_lex = false; + bool left_close = false; + bool right_close = false; + bool is_rev = false; + size_t argc = client->argv_.size(); + if (argc >= 5) { + size_t index = 4; + while (index < argc) { + if (strcasecmp(client->argv_[index].data(), "byscore") == 0) { + by_score = true; + } else if (strcasecmp(client->argv_[index].data(), "bylex") == 0) { + by_lex = true; + } else if (strcasecmp(client->argv_[index].data(), "rev") == 0) { + is_rev = true; + } else if (strcasecmp(client->argv_[index].data(), "withscores") == 0) { + with_scores = true; + } else if (strcasecmp(client->argv_[index].data(), "limit") == 0) { + if (index + 3 > argc) { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + index++; + if (pstd::String2int(client->argv_[index].data(), client->argv_[index].size(), &offset) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + index++; + if (pstd::String2int(client->argv_[index].data(), client->argv_[index].size(), &count) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + } else { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + index++; + } + } + if (by_score && by_lex) { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + + int32_t ret = 0; + std::string lex_min; + std::string lex_max; + if (by_lex) { + ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &lex_min, &lex_max); + if (ret == -1) { + client->SetRes(CmdRes::kErrOther, "min or max not valid string range item"); + return; + } + } else { + ret = DoScoreStrRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &start, &stop); + if (ret == -1) { + client->SetRes(CmdRes::kErrOther, "start or stop is not a float"); + return; + } + } + + std::vector score_members; + std::vector lex_members; + storage::Status s; + if (!is_rev) { + if (by_score) { + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->GetStorage() + ->ZRangebyscore(client->Key(), start, stop, left_close, right_close, &score_members); + } else if (by_lex) { + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->GetStorage() + ->ZRangebylex(client->Key(), lex_min, lex_max, left_close, right_close, &lex_members); + } else { + s = PSTORE.GetBackend(client->GetCurrentDB())->GetStorage()->ZRange(client->Key(), start, stop, &score_members); + } + } else { + if (by_score) { + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->GetStorage() + ->ZRevrangebyscore(client->Key(), start, stop, left_close, right_close, &score_members); + } else if (by_lex) { + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->GetStorage() + ->ZRangebylex(client->Key(), lex_min, lex_max, left_close, right_close, &lex_members); + } else { + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->GetStorage() + ->ZRevrange(client->Key(), start, stop, &score_members); + } + } + if (!s.ok() && !s.IsNotFound()) { + client->SetRes(CmdRes::kErrOther, s.ToString()); + return; + } + FitLimit(count, offset, static_cast(score_members.size())); + size_t m_start = offset; + size_t m_end = offset + count; + if (by_lex) { + if (with_scores) { + client->SetRes(CmdRes::kSyntaxErr, "by lex not support with scores"); + } else { + client->AppendArrayLen(count); + for (; m_start < m_end; m_start++) { + client->AppendContent(lex_members[m_start]); + } + } + } else { + if (with_scores) { + char buf[32]; + int64_t len = 0; + client->AppendArrayLen(count * 2); + for (; m_start < m_end; m_start++) { + client->AppendStringLenUint64(score_members[m_start].member.size()); + client->AppendContent(score_members[m_start].member); + len = pstd::D2string(buf, sizeof(buf), score_members[m_start].score); + client->AppendStringLen(len); + client->AppendContent(buf); + } + } else { + client->AppendArrayLen(count); + for (; m_start < m_end; m_start++) { + client->AppendStringLenUint64(score_members[m_start].member.size()); + client->AppendContent(score_members[m_start].member); + } + } + } +} + +ZScoreCmd::ZScoreCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsReadonly, kAclCategoryRead | kAclCategoryString) {} + +bool ZScoreCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZScoreCmd::DoCmd(PClient* client) { + double score = 0; + + storage::Status s; + s = PSTORE.GetBackend(client->GetCurrentDB())->GetStorage()->ZScore(client->Key(), client->argv_[2], &score); + if (s.ok() || s.IsNotFound()) { + client->AppendString(std::to_string(score)); + } else { + client->SetRes(CmdRes::kErrOther, s.ToString()); + } +} + +ZRevrangebyscoreCmd::ZRevrangebyscoreCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} -bool ZRevRangeByScoreCmd::DoInitial(PClient* client) { +bool ZRevrangebyscoreCmd::DoInitial(PClient* client) { client->SetKey(client->argv_[1]); return true; } -void ZRevRangeByScoreCmd::DoCmd(PClient* client) { +void ZRevrangebyscoreCmd::DoCmd(PClient* client) { double min_score = 0; double max_score = 0; bool right_close = true; @@ -339,4 +567,151 @@ void ZRevRangeByScoreCmd::DoCmd(PClient* client) { } } +ZRangebylexCmd::ZRangebylexCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} + +bool ZRangebylexCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZRangebylexCmd::DoCmd(PClient* client) { + if (strcasecmp(client->argv_[2].data(), "+") == 0 || strcasecmp(client->argv_[3].data(), "-") == 0) { + client->AppendContent("*0"); + } + + size_t argc = client->argv_.size(); + int64_t count = -1; + int64_t offset = 0; + bool left_close = true; + bool right_close = true; + if (argc == 7 && strcasecmp(client->argv_[4].data(), "limit") == 0) { + if (pstd::String2int(client->argv_[5].data(), client->argv_[5].size(), &offset) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + if (pstd::String2int(client->argv_[6].data(), client->argv_[6].size(), &count) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + } else if (argc == 4) { + } else { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + + std::string min_member; + std::string max_member; + int32_t ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &min_member, &max_member); + if (ret == -1) { + client->SetRes(CmdRes::kErrOther, "min or max not valid string range item"); + return; + } + std::vector members; + storage::Status s; + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->GetStorage() + ->ZRangebylex(client->Key(), min_member, max_member, left_close, right_close, &members); + if (!s.ok() && !s.IsNotFound()) { + client->SetRes(CmdRes::kErrOther, s.ToString()); + return; + } + + FitLimit(count, offset, static_cast(members.size())); + size_t index = offset; + size_t end = offset + count; + + client->AppendArrayLen(static_cast(members.size())); + for (; index < end; index++) { + client->AppendStringLenUint64(members[index].size()); + client->AppendContent(members[index]); + } +} + +ZRevrangebylexCmd::ZRevrangebylexCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} + +bool ZRevrangebylexCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZRevrangebylexCmd::DoCmd(PClient* client) { + if (strcasecmp(client->argv_[2].data(), "+") == 0 || strcasecmp(client->argv_[3].data(), "-") == 0) { + client->AppendContent("*0"); + } + + size_t argc = client->argv_.size(); + int64_t count = -1; + int64_t offset = 0; + bool left_close = true; + bool right_close = true; + if (argc == 7 && strcasecmp(client->argv_[4].data(), "limit") == 0) { + if (pstd::String2int(client->argv_[5].data(), client->argv_[5].size(), &offset) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + if (pstd::String2int(client->argv_[6].data(), client->argv_[6].size(), &count) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + } else if (argc == 4) { + } else { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + + std::string min_member; + std::string max_member; + int32_t ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &min_member, &max_member); + std::vector members; + storage::Status s; + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->GetStorage() + ->ZRangebylex(client->Key(), min_member, max_member, left_close, right_close, &members); + if (!s.ok() && !s.IsNotFound()) { + client->SetRes(CmdRes::kErrOther, s.ToString()); + return; + } + + FitLimit(count, offset, static_cast(members.size())); + size_t index = offset + count - 1; + size_t start = offset; + client->AppendArrayLen(static_cast(members.size())); + for (; index >= start; index--) { + client->AppendStringLenUint64(members[index].size()); + client->AppendContent(members[index]); + } +} + +ZRemrangebyscoreCmd::ZRemrangebyscoreCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} + +bool ZRemrangebyscoreCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZRemrangebyscoreCmd::DoCmd(PClient* client) { + double min_score = 0; + double max_score = 0; + bool left_close = true; + bool right_close = true; + int32_t ret = DoScoreStrRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &min_score, &max_score); + if (ret == -1) { + client->SetRes(CmdRes::kErrOther, "min or max is not a float"); + return; + } + + int32_t s_ret = 0; + storage::Status s = PSTORE.GetBackend(client->GetCurrentDB()) + ->GetStorage() + ->ZRemrangebyscore(client->Key(), min_score, max_score, left_close, right_close, &s_ret); + if (s.ok()) { + client->AppendInteger(s_ret); + } else { + client->SetRes(CmdRes::kErrOther, s.ToString()); + } +} + } // namespace pikiwidb \ No newline at end of file diff --git a/src/cmd_zset.h b/src/cmd_zset.h index ddef7b956..c4ed6ba55 100644 --- a/src/cmd_zset.h +++ b/src/cmd_zset.h @@ -45,9 +45,20 @@ class ZRangebyscoreCmd : public BaseCmd { void DoCmd(PClient *client) override; }; -class ZRevRangeByScoreCmd : public BaseCmd { +class ZRemrangebyrankCmd : public BaseCmd { public: - ZRevRangeByScoreCmd(const std::string &name, int16_t arity); + ZRemrangebyrankCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + +class ZRevrangebyscoreCmd : public BaseCmd { + public: + ZRevrangebyscoreCmd(const std::string &name, int16_t arity); protected: bool DoInitial(PClient *client) override; @@ -67,4 +78,59 @@ class ZCardCmd : public BaseCmd { void DoCmd(PClient *client) override; }; +class ZRangeCmd : public BaseCmd { + public: + ZRangeCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + +class ZScoreCmd : public BaseCmd { + public: + ZScoreCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + +class ZRangebylexCmd : public BaseCmd { + public: + ZRangebylexCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + +class ZRevrangebylexCmd : public BaseCmd { + public: + ZRevrangebylexCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + +class ZRemrangebyscoreCmd : public BaseCmd { + public: + ZRemrangebyscoreCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + } // namespace pikiwidb \ No newline at end of file diff --git a/src/storage/src/lists_meta_value_format.h b/src/storage/src/lists_meta_value_format.h index e41678897..50b1fa7b9 100644 --- a/src/storage/src/lists_meta_value_format.h +++ b/src/storage/src/lists_meta_value_format.h @@ -31,7 +31,6 @@ class ListsMetaValue : public InternalValue { size_t usize = user_value_.size(); size_t needed = usize + kVersionLength + 2 * kListValueIndexLength + kSuffixReserveLength + 2 * kTimestampLength; char* dst = ReAllocIfNeeded(needed); - char* start_pos = dst; memcpy(dst, user_value_.data(), usize); dst += usize; @@ -46,7 +45,7 @@ class ListsMetaValue : public InternalValue { EncodeFixed64(dst, ctime_); dst += kTimestampLength; EncodeFixed64(dst, etime_); - return rocksdb::Slice(start_pos, needed); + return rocksdb::Slice(start_, needed); } uint64_t UpdateVersion() { @@ -124,7 +123,7 @@ class ParsedListsMetaValue : public ParsedInternalValue { offset += kTimestampLength; etime_ = DecodeFixed64(internal_value_slice.data() + offset); } - count_ = DecodeFixed64(internal_value_slice.data() + 1); + count_ = DecodeFixed64(internal_value_slice.data() + TYPE_SIZE); } void StripSuffix() override { diff --git a/src/storage/src/redis_hashes.cc b/src/storage/src/redis_hashes.cc index 7f5eb3141..11498032a 100644 --- a/src/storage/src/redis_hashes.cc +++ b/src/storage/src/redis_hashes.cc @@ -364,7 +364,7 @@ Status Redis::HIncrby(const Slice& key, const Slice& field, int64_t value, int64 } } else if (s.IsNotFound()) { EncodeFixed8(meta_value_buf, 'h'); - EncodeFixed32(meta_value_buf + 1, 1); + EncodeFixed32(meta_value_buf + TYPE_SIZE, 1); HashesMetaValue hashes_meta_value(Slice(meta_value_buf, 5)); version = hashes_meta_value.UpdateVersion(); batch.Put(handles_[kMetaCF], base_meta_key.Encode(), hashes_meta_value.Encode()); @@ -450,7 +450,7 @@ Status Redis::HIncrbyfloat(const Slice& key, const Slice& field, const Slice& by } } else if (s.IsNotFound()) { EncodeFixed8(meta_value_buf, 'h'); - EncodeFixed32(meta_value_buf + 1, 1); + EncodeFixed32(meta_value_buf + TYPE_SIZE, 1); HashesMetaValue hashes_meta_value(Slice(meta_value_buf, 5)); version = hashes_meta_value.UpdateVersion(); batch.Put(handles_[kMetaCF], base_meta_key.Encode(), hashes_meta_value.Encode()); @@ -641,7 +641,7 @@ Status Redis::HMSet(const Slice& key, const std::vector& fvs) { } } else if (s.IsNotFound()) { EncodeFixed8(meta_value_buf, 'h'); - EncodeFixed32(meta_value_buf + 1, filtered_fvs.size()); + EncodeFixed32(meta_value_buf + TYPE_SIZE, filtered_fvs.size()); HashesMetaValue hashes_meta_value(Slice(meta_value_buf, 5)); version = hashes_meta_value.UpdateVersion(); batch.Put(handles_[kMetaCF], base_meta_key.Encode(), hashes_meta_value.Encode()); @@ -712,7 +712,7 @@ Status Redis::HSet(const Slice& key, const Slice& field, const Slice& value, int } } else if (s.IsNotFound()) { EncodeFixed8(meta_value_buf, 'h'); - EncodeFixed32(meta_value_buf + 1, 1); + EncodeFixed32(meta_value_buf + TYPE_SIZE, 1); HashesMetaValue hashes_meta_value(Slice(meta_value_buf, 5)); version = hashes_meta_value.UpdateVersion(); batch.Put(handles_[kMetaCF], base_meta_key.Encode(), hashes_meta_value.Encode()); @@ -772,7 +772,7 @@ Status Redis::HSetnx(const Slice& key, const Slice& field, const Slice& value, i } } else if (s.IsNotFound()) { EncodeFixed8(meta_value_buf, 'h'); - EncodeFixed32(meta_value_buf + 1, 1); + EncodeFixed32(meta_value_buf + TYPE_SIZE, 1); HashesMetaValue hashes_meta_value(Slice(meta_value_buf, 5)); version = hashes_meta_value.UpdateVersion(); batch.Put(handles_[kMetaCF], base_meta_key.Encode(), hashes_meta_value.Encode()); diff --git a/src/storage/src/redis_lists.cc b/src/storage/src/redis_lists.cc index 2e20aa48d..089198def 100644 --- a/src/storage/src/redis_lists.cc +++ b/src/storage/src/redis_lists.cc @@ -360,7 +360,7 @@ Status Redis::LPush(const Slice& key, const std::vector& values, ui } else if (s.IsNotFound()) { char str[9]; EncodeFixed8(str, 'l'); - EncodeFixed64(str + 1, values.size()); + EncodeFixed64(str + TYPE_SIZE, values.size()); ListsMetaValue lists_meta_value(Slice(str, 9)); version = lists_meta_value.UpdateVersion(); for (const auto& value : values) { @@ -371,7 +371,6 @@ Status Redis::LPush(const Slice& key, const std::vector& values, ui batch.Put(handles_[kListsDataCF], lists_data_key.Encode(), i_val.Encode()); } batch.Put(handles_[kMetaCF], base_meta_key.Encode(), lists_meta_value.Encode()); - // std::cout << "list_meta_value: " << lists_meta_value.Encode().ToStringView() << std::endl; *ret = lists_meta_value.RightIndex() - lists_meta_value.LeftIndex() - 1; } else { return s; @@ -916,7 +915,7 @@ Status Redis::RPoplpush(const Slice& source, const Slice& destination, std::stri } else if (s.IsNotFound()) { char str[9]; EncodeFixed8(str, 'l'); - EncodeFixed64(str + 1, 1); + EncodeFixed64(str + TYPE_SIZE, 1); ListsMetaValue lists_meta_value(Slice(str, 9)); version = lists_meta_value.UpdateVersion(); uint64_t target_index = lists_meta_value.LeftIndex(); @@ -971,7 +970,7 @@ Status Redis::RPush(const Slice& key, const std::vector& values, ui } else if (s.IsNotFound()) { char str[9]; EncodeFixed8(str, 'l'); - EncodeFixed64(str + 1, values.size()); + EncodeFixed64(str + TYPE_SIZE, values.size()); ListsMetaValue lists_meta_value(Slice(str, 9)); version = lists_meta_value.UpdateVersion(); for (const auto& value : values) { diff --git a/src/storage/src/redis_sets.cc b/src/storage/src/redis_sets.cc index 725b33a99..476a686a4 100644 --- a/src/storage/src/redis_sets.cc +++ b/src/storage/src/redis_sets.cc @@ -178,7 +178,7 @@ rocksdb::Status Redis::SAdd(const Slice& key, const std::vector& me } else if (s.IsNotFound()) { char str[5]; EncodeFixed8(str, 's'); - EncodeFixed32(str + 1, filtered_members.size()); + EncodeFixed32(str + TYPE_SIZE, filtered_members.size()); SetsMetaValue sets_meta_value(Slice(str, 5)); version = sets_meta_value.UpdateVersion(); batch.Put(handles_[kMetaCF], base_meta_key.Encode(), sets_meta_value.Encode()); @@ -386,7 +386,7 @@ rocksdb::Status Redis::SDiffstore(const Slice& destination, const std::vector& score_membe } else if (s.IsNotFound()) { char str[5]; EncodeFixed8(str, 'z'); - EncodeFixed32(str + 1, filtered_score_members.size()); + EncodeFixed32(str + TYPE_SIZE, filtered_score_members.size()); ZSetsMetaValue zsets_meta_value(Slice(str, 5)); version = zsets_meta_value.UpdateVersion(); batch.Put(handles_[kMetaCF], base_meta_key.Encode(), zsets_meta_value.Encode()); @@ -454,9 +454,11 @@ Status Redis::ZIncrby(const Slice& key, const Slice& member, double increment, d return s; } } else if (s.IsNotFound()) { - char buf[8]; - EncodeFixed32(buf, 1); - ZSetsMetaValue zsets_meta_value(Slice(buf, sizeof(int32_t))); + char str[5]; + EncodeFixed8(str, 'z'); + EncodeFixed32(str + TYPE_SIZE, 1); + ZSetsMetaValue zsets_meta_value(Slice(str, 5)); + version = zsets_meta_value.UpdateVersion(); batch.Put(handles_[kMetaCF], base_meta_key.Encode(), zsets_meta_value.Encode()); score = increment; @@ -1230,7 +1232,7 @@ Status Redis::ZUnionstore(const Slice& destination, const std::vector(final_score_members.size())); batch.Put(handles_[kMetaCF], base_destination.Encode(), meta_value); } else { - char buf[4]; - EncodeFixed32(buf, final_score_members.size()); - ZSetsMetaValue zsets_meta_value(Slice(buf, sizeof(int32_t))); + char str[5]; + EncodeFixed8(str, 'z'); + EncodeFixed32(str + TYPE_SIZE, final_score_members.size()); + ZSetsMetaValue zsets_meta_value(Slice(str, 5)); version = zsets_meta_value.UpdateVersion(); batch.Put(handles_[kMetaCF], base_destination.Encode(), zsets_meta_value.Encode()); } diff --git a/tests/key_test.go b/tests/key_test.go index 64f6df371..844e2c9f2 100644 --- a/tests/key_test.go +++ b/tests/key_test.go @@ -240,4 +240,18 @@ var _ = Describe("Keyspace", Ordered, func() { // // del keys // Expect(client.Del(ctx, "a1", "k1", "k2", "k3", "k4", "k5").Err()).NotTo(HaveOccurred()) //}) + + It("should pexpire", func() { + Expect(client.Set(ctx, DefaultKey, DefaultValue, 0).Val()).To(Equal(OK)) + Expect(client.PExpire(ctx, DefaultKey, 3000*time.Millisecond).Val()).To(Equal(true)) + Expect(client.PTTL(ctx, DefaultKey).Val()).NotTo(Equal(time.Duration(-2))) + + time.Sleep(4 * time.Second) + Expect(client.PTTL(ctx, DefaultKey).Val()).To(Equal(time.Duration(-2))) + Expect(client.Get(ctx, DefaultKey).Err()).To(MatchError(redis.Nil)) + Expect(client.Exists(ctx, DefaultKey).Val()).To(Equal(int64(0))) + + Expect(client.Do(ctx, "pexpire", DefaultKey, "err").Err()).To(MatchError("ERR value is not an integer or out of range")) + }) + }) diff --git a/tests/zset_test.go b/tests/zset_test.go index 5cdf309f6..e60c1ca7c 100644 --- a/tests/zset_test.go +++ b/tests/zset_test.go @@ -175,7 +175,7 @@ var _ = Describe("Zset", Ordered, func() { Expect(zRevRange.Val()).To(Equal([]string{"two", "one"})) }) - It("should ZRevRangeByScore", func() { + It("should ZRemRangeByRank", func() { err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() Expect(err).NotTo(HaveOccurred()) err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() @@ -183,20 +183,36 @@ var _ = Describe("Zset", Ordered, func() { err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() Expect(err).NotTo(HaveOccurred()) - vals, err := client.ZRevRangeByScore( - ctx, "zset", &redis.ZRangeBy{Max: "+inf", Min: "-inf"}).Result() + zRemRangeByRank := client.ZRemRangeByRank(ctx, "zset", 0, 1) + Expect(zRemRangeByRank.Err()).NotTo(HaveOccurred()) + Expect(zRemRangeByRank.Val()).To(Equal(int64(2))) + + vals, err := client.ZRangeWithScores(ctx, "zset", 0, -1).Result() Expect(err).NotTo(HaveOccurred()) - Expect(vals).To(Equal([]string{"three", "two", "one"})) + Expect(vals).To(Equal([]redis.Z{{ + Score: 3, + Member: "three", + }})) + }) - vals, err = client.ZRevRangeByScore( - ctx, "zset", &redis.ZRangeBy{Max: "2", Min: "(1"}).Result() + It("should ZRevRangeByScore", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() Expect(err).NotTo(HaveOccurred()) - Expect(vals).To(Equal([]string{"two"})) - vals, err = client.ZRevRangeByScore( - ctx, "zset", &redis.ZRangeBy{Max: "(2", Min: "(1"}).Result() + zRemRangeByRank := client.ZRemRangeByRank(ctx, "zset", 0, 1) + Expect(zRemRangeByRank.Err()).NotTo(HaveOccurred()) + Expect(zRemRangeByRank.Val()).To(Equal(int64(2))) + + vals, err := client.ZRangeWithScores(ctx, "zset", 0, -1).Result() Expect(err).NotTo(HaveOccurred()) - Expect(vals).To(Equal([]string{})) + Expect(vals).To(Equal([]redis.Z{{ + Score: 3, + Member: "three", + }})) }) It("should ZCard", func() { @@ -215,4 +231,182 @@ var _ = Describe("Zset", Ordered, func() { Expect(err).NotTo(HaveOccurred()) Expect(card).To(Equal(int64(2))) }) + + It("should ZRange", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() + Expect(err).NotTo(HaveOccurred()) + + zRange := client.ZRange(ctx, "zset", 0, -1) + Expect(zRange.Err()).NotTo(HaveOccurred()) + Expect(zRange.Val()).To(Equal([]string{"one", "two", "three"})) + + zRange = client.ZRange(ctx, "zset", 2, 3) + Expect(zRange.Err()).NotTo(HaveOccurred()) + Expect(zRange.Val()).To(Equal([]string{"three"})) + + zRange = client.ZRange(ctx, "zset", -2, -1) + Expect(zRange.Err()).NotTo(HaveOccurred()) + Expect(zRange.Val()).To(Equal([]string{"two", "three"})) + }) + + It("should ZRangeWithScores", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() + Expect(err).NotTo(HaveOccurred()) + + vals, err := client.ZRangeWithScores(ctx, "zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 1, + Member: "one", + }, { + Score: 2, + Member: "two", + }, { + Score: 3, + Member: "three", + }})) + + vals, err = client.ZRangeWithScores(ctx, "zset", 2, 3).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{Score: 3, Member: "three"}})) + + vals, err = client.ZRangeWithScores(ctx, "zset", -2, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 2, + Member: "two", + }, { + Score: 3, + Member: "three", + }})) + }) + + It("should ZRangeByScoreWithScoresMap", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() + Expect(err).NotTo(HaveOccurred()) + + vals, err := client.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ + Min: "-inf", + Max: "+inf", + }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 1, + Member: "one", + }, { + Score: 2, + Member: "two", + }, { + Score: 3, + Member: "three", + }})) + + vals, err = client.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ + Min: "1", + Max: "2", + }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 1, + Member: "one", + }, { + Score: 2, + Member: "two", + }})) + + vals, err = client.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ + Min: "(1", + Max: "2", + }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{Score: 2, Member: "two"}})) + + vals, err = client.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ + Min: "(1", + Max: "(2", + }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{})) + }) + + It("should ZRangeByLex", func() { + err := client.ZAdd(ctx, "zsetrangebylex", redis.Z{ + Score: 0, + Member: "a", + }).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zsetrangebylex", redis.Z{ + Score: 0, + Member: "b", + }).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zsetrangebylex", redis.Z{ + Score: 0, + Member: "c", + }).Err() + Expect(err).NotTo(HaveOccurred()) + + zRangeByLex := client.ZRangeByLex(ctx, "zsetrangebylex", &redis.ZRangeBy{ + Min: "-", + Max: "+", + }) + Expect(zRangeByLex.Err()).NotTo(HaveOccurred()) + Expect(zRangeByLex.Val()).To(Equal([]string{"a", "b", "c"})) + + zRangeByLex = client.ZRangeByLex(ctx, "zsetrangebylex", &redis.ZRangeBy{ + Min: "[a", + Max: "[b", + }) + Expect(zRangeByLex.Err()).NotTo(HaveOccurred()) + Expect(zRangeByLex.Val()).To(Equal([]string{"a", "b"})) + + zRangeByLex = client.ZRangeByLex(ctx, "zsetrangebylex", &redis.ZRangeBy{ + Min: "(a", + Max: "[b", + }) + Expect(zRangeByLex.Err()).NotTo(HaveOccurred()) + Expect(zRangeByLex.Val()).To(Equal([]string{"b"})) + + zRangeByLex = client.ZRangeByLex(ctx, "zsetrangebylex", &redis.ZRangeBy{ + Min: "(a", + Max: "(b", + }) + Expect(zRangeByLex.Err()).NotTo(HaveOccurred()) + Expect(zRangeByLex.Val()).To(Equal([]string{})) + }) + + It("should ZRemRangeByScore", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() + Expect(err).NotTo(HaveOccurred()) + + zRemRangeByScore := client.ZRemRangeByScore(ctx, "zset", "-inf", "(2") + Expect(zRemRangeByScore.Err()).NotTo(HaveOccurred()) + Expect(zRemRangeByScore.Val()).To(Equal(int64(1))) + + vals, err := client.ZRangeWithScores(ctx, "zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 2, + Member: "two", + }, { + Score: 3, + Member: "three", + }})) + }) })