From e05878dfe99c232be3a6f40b6dc9ab73785c7db0 Mon Sep 17 00:00:00 2001 From: Qx Date: Thu, 2 Nov 2023 13:58:19 +0800 Subject: [PATCH] feat:Subcommands support separate command id (#24) * feat:Subcommands support separate command ids * fix spelling error,optimize GetCommand function return value * fix check params number error --- src/base_cmd.cc | 27 ++++++++++++++++++++++++-- src/base_cmd.h | 31 ++++++++++++++++++++++------- src/client.cc | 30 ++++++++++++++++------------ src/client.h | 2 +- src/cmd_admin.cc | 42 +++++++++------------------------------- src/cmd_admin.h | 31 ++++++++++++++++++++++------- src/cmd_kv.cc | 4 ++-- src/cmd_kv.h | 4 ++-- src/cmd_table_manager.cc | 18 +++++++++++++---- src/cmd_table_manager.h | 2 +- 10 files changed, 120 insertions(+), 71 deletions(-) diff --git a/src/base_cmd.cc b/src/base_cmd.cc index cce60d7ec..a8ba77419 100644 --- a/src/base_cmd.cc +++ b/src/base_cmd.cc @@ -48,8 +48,7 @@ bool BaseCmd::HasFlag(uint32_t flag) const { return flag_ & flag; } void BaseCmd::SetFlag(uint32_t flag) { flag_ |= flag; } void BaseCmd::ResetFlag(uint32_t flag) { flag_ &= ~flag; } bool BaseCmd::HasSubCommand() const { return false; } -std::vector BaseCmd::SubCommand() const { return {}; } -int8_t BaseCmd::SubCmdIndex(const std::string& cmdName) { return -1; } +BaseCmd* BaseCmd::GetSubCmd(const std::string& cmdName) { return nullptr; } uint32_t BaseCmd::AclCategory() const { return aclCategory_; } void BaseCmd::AddAclCategory(uint32_t aclCategory) { aclCategory_ |= aclCategory; } std::string BaseCmd::Name() const { return name_; } @@ -58,4 +57,28 @@ std::string BaseCmd::Name() const { return name_; } // std::shared_ptr BaseCommand::GetResp() { return resp_.lock(); } uint32_t BaseCmd::GetCmdId() const { return cmdId_; } +// BaseCmdGroup +BaseCmdGroup::BaseCmdGroup(const std::string& name, uint32_t flag) : BaseCmdGroup(name, -2, flag) {} +BaseCmdGroup::BaseCmdGroup(const std::string& name, int16_t arity, uint32_t flag) : BaseCmd(name, arity, flag, 0) {} + +void BaseCmdGroup::AddSubCmd(std::unique_ptr cmd) { subCmds_[cmd->Name()] = std::move(cmd); } + +BaseCmd* BaseCmdGroup::GetSubCmd(const std::string& cmdName) { + auto subCmd = subCmds_.find(cmdName); + if (subCmd == subCmds_.end()) { + return nullptr; + } + return subCmd->second.get(); +} + +bool BaseCmdGroup::DoInitial(CmdContext& ctx) { + ctx.subCmd_ = ctx.argv_[1]; + std::transform(ctx.argv_[1].begin(), ctx.argv_[1].end(), ctx.subCmd_.begin(), ::tolower); + if (!subCmds_.contains(ctx.subCmd_)) { + ctx.SetRes(CmdRes::kSyntaxErr, ctx.argv_[0] + " unknown subcommand for '" + ctx.subCmd_ + "'"); + return false; + } + return true; +} + } // namespace pikiwidb \ No newline at end of file diff --git a/src/base_cmd.h b/src/base_cmd.h index e40092348..11241472e 100644 --- a/src/base_cmd.h +++ b/src/base_cmd.h @@ -5,10 +5,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef PIKIWIDB_SRC_BASE_CMD_H -#define PIKIWIDB_SRC_BASE_CMD_H +#pragma once #include +#include #include #include #include @@ -159,9 +159,8 @@ class BaseCmd : public std::enable_shared_from_this { // then these functions do not need to be implemented. // If it is a subcommand, you need to implement these functions // e.g: CmdConfig is a subcommand, and the subcommand is set and get - virtual bool HasSubCommand() const; // The command is there a sub command - virtual std::vector SubCommand() const; // Get command is there a sub command - virtual int8_t SubCmdIndex(const std::string& cmdName); // if the command no subCommand,return -1; + virtual bool HasSubCommand() const; // The command is there a sub command + virtual BaseCmd* GetSubCmd(const std::string& cmdName); uint32_t AclCategory() const; void AddAclCategory(uint32_t aclCategory); @@ -186,7 +185,6 @@ class BaseCmd : public std::enable_shared_from_this { std::string name_; int16_t arity_ = 0; uint32_t flag_ = 0; - std::vector subCmdName_; // sub command name, may be empty // CmdRes res_; // std::string dbName_; @@ -207,5 +205,24 @@ class BaseCmd : public std::enable_shared_from_this { // BaseCmd& operator=(const BaseCmd&); }; +class BaseCmdGroup : public BaseCmd { + public: + BaseCmdGroup(const std::string& name, uint32_t flag); + BaseCmdGroup(const std::string& name, int16_t arity, uint32_t flag); + + ~BaseCmdGroup() override = default; + + void AddSubCmd(std::unique_ptr cmd); + BaseCmd* GetSubCmd(const std::string& cmdName) override; + + // group cmd this function will not be called + void DoCmd(CmdContext& ctx) override{}; + + // group cmd this function will not be called + bool DoInitial(CmdContext& ctx) override; + + private: + std::map> subCmds_; +}; + } // namespace pikiwidb -#endif // PIKIWIDB_SRC_BASE_CMD_H diff --git a/src/client.cc b/src/client.cc index 88c34199c..41496132b 100644 --- a/src/client.cc +++ b/src/client.cc @@ -196,7 +196,7 @@ int PClient::handlePacket(pikiwidb::TcpConnection* obj, const char* start, int b const PCommandInfo* info = PCommandTable::GetCommandInfo(cmd); if (!info) { // 如果这个命令不存在,那么就走新的命令处理流程 - handlePacketNew(obj, params, cmd); + handlePacketNew(params, cmd); return static_cast(ptr - start); } @@ -241,26 +241,32 @@ int PClient::handlePacket(pikiwidb::TcpConnection* obj, const char* start, int b // 为了兼容老的命令处理流程,新的命令处理流程在这里 // 后面可以把client这个类重构,完整的支持新的命令处理流程 -int PClient::handlePacketNew(pikiwidb::TcpConnection* obj, const std::vector& params, - const std::string& cmd) { - auto cmdPtr = g_pikiwidb->GetCmdTableManager().GetCommand(cmd); +int PClient::handlePacketNew(const std::vector& params, const std::string& cmd) { + CmdContext ctx; + ctx.client_ = this; + // 因为 params 是一个引用,不能直接传给 ctx.argv_,所以需要拷贝一份,后面可以优化 + std::vector argv = params; + ctx.argv_ = argv; + + auto [cmdPtr, ret] = g_pikiwidb->GetCmdTableManager().GetCommand(cmd, ctx); if (!cmdPtr) { - ReplyError(PError_unknowCmd, &reply_); + if (ret == CmdRes::kInvalidParameter) { + ctx.SetRes(CmdRes::kInvalidParameter); + } else { + ctx.SetRes(CmdRes::kSyntaxErr, "unknown command '" + cmd + "'"); + } + reply_.PushData(ctx.message().data(), ctx.message().size()); return 0; } if (!cmdPtr->CheckArg(params.size())) { - ReplyError(PError_param, &reply_); + ctx.SetRes(CmdRes::kSyntaxErr, "wrong number of arguments for '" + cmd + "' command"); + reply_.PushData(ctx.message().data(), ctx.message().size()); return 0; } - CmdContext ctx; - ctx.client_ = this; - // 因为 params 是一个引用,不能直接传给 ctx.argv_,所以需要拷贝一份,后面可以优化 - std::vector argv = params; - ctx.argv_ = argv; - + // execute a specific command cmdPtr->Execute(ctx); reply_.PushData(ctx.message().data(), ctx.message().size()); diff --git a/src/client.h b/src/client.h index aefe7b19e..ad1ef11e5 100644 --- a/src/client.h +++ b/src/client.h @@ -103,7 +103,7 @@ class PClient : public std::enable_shared_from_this { private: std::shared_ptr getTcpConnection() const { return tcp_connection_.lock(); } int handlePacket(pikiwidb::TcpConnection*, const char*, int); - int handlePacketNew(pikiwidb::TcpConnection* obj, const std::vector& params, const std::string& cmd); + int handlePacketNew(const std::vector& params, const std::string& cmd); int processInlineCmd(const char*, size_t, std::vector&); void reset(); bool isPeerMaster() const; diff --git a/src/cmd_admin.cc b/src/cmd_admin.cc index f36748c8c..7e131aff4 100644 --- a/src/cmd_admin.cc +++ b/src/cmd_admin.cc @@ -9,48 +9,24 @@ namespace pikiwidb { -CmdConfig::CmdConfig(const std::string& name, int arity) : BaseCmd(name, arity, CmdFlagsAdmin, AclCategoryAdmin) { +CmdConfig::CmdConfig(const std::string& name, int arity) : BaseCmdGroup(name, CmdFlagsAdmin, AclCategoryAdmin) { subCmd_ = {"set", "get"}; } bool CmdConfig::HasSubCommand() const { return true; } -std::vector CmdConfig::SubCommand() const { return subCmd_; } +CmdConfigGet::CmdConfigGet(const std::string& name, int16_t arity) + : BaseCmd(name, arity, CmdFlagsAdmin | CmdFlagsWrite, AclCategoryAdmin) {} -int8_t CmdConfig::SubCmdIndex(const std::string& cmdName) { - for (size_t i = 0; i < subCmd_.size(); i++) { - if (subCmd_[i] == cmdName) { - return i; - } - } - return -1; -} +bool CmdConfigGet::DoInitial(CmdContext& ctx) { return true; } -bool CmdConfig::DoInitial(pikiwidb::CmdContext& ctx) { - ctx.subCmd_ = ctx.argv_[1]; - std::transform(ctx.argv_[1].begin(), ctx.argv_[1].end(), ctx.subCmd_.begin(), ::tolower); - if (ctx.subCmd_ == subCmd_[0] || ctx.subCmd_ == subCmd_[1]) { - if (ctx.argv_.size() < 3) { - ctx.SetRes(CmdRes::kInvalidParameter, "config " + ctx.subCmd_); - return false; - } - } - - return true; -} +void CmdConfigGet::DoCmd(CmdContext& ctx) { ctx.AppendString("config cmd in development"); } -void CmdConfig::DoCmd(pikiwidb::CmdContext& ctx) { - if (ctx.subCmd_ == subCmd_[0]) { - Set(ctx); - } else if (ctx.subCmd_ == subCmd_[1]) { - Get(ctx); - } else { - ctx.SetRes(CmdRes::kSyntaxErr, "config error"); - } -} +CmdConfigSet::CmdConfigSet(const std::string& name, int16_t arity) + : BaseCmd(name, arity, CmdFlagsAdmin, AclCategoryAdmin) {} -void CmdConfig::Get(CmdContext& ctx) { ctx.AppendString("config cmd in development"); } +bool CmdConfigSet::DoInitial(CmdContext& ctx) { return true; } -void CmdConfig::Set(CmdContext& ctx) { ctx.AppendString("config cmd in development"); } +void CmdConfigSet::DoCmd(CmdContext& ctx) { ctx.AppendString("config cmd in development"); } } // namespace pikiwidb \ No newline at end of file diff --git a/src/cmd_admin.h b/src/cmd_admin.h index 62add18b1..fd094c0d5 100644 --- a/src/cmd_admin.h +++ b/src/cmd_admin.h @@ -12,24 +12,41 @@ namespace pikiwidb { -class CmdConfig : public BaseCmd { +class CmdConfig : public BaseCmdGroup { public: CmdConfig(const std::string& name, int arity); bool HasSubCommand() const override; - std::vector SubCommand() const override; - int8_t SubCmdIndex(const std::string& cmdName) override; protected: - bool DoInitial(CmdContext& ctx) override; + bool DoInitial(CmdContext& ctx) override { return true; }; private: std::vector subCmd_; - void DoCmd(CmdContext& ctx) override; + void DoCmd(CmdContext& ctx) override{}; +}; + +class CmdConfigGet : public BaseCmd { + public: + CmdConfigGet(const std::string& name, int16_t arity); + + protected: + bool DoInitial(CmdContext& ctx) override; + + private: + void DoCmd(pikiwidb::CmdContext& ctx) override; +}; + +class CmdConfigSet : public BaseCmd { + public: + CmdConfigSet(const std::string& name, int16_t arity); - void Get(CmdContext& ctx); - void Set(CmdContext& ctx); + protected: + bool DoInitial(CmdContext& ctx) override; + + private: + void DoCmd(pikiwidb::CmdContext& ctx) override; }; } // namespace pikiwidb diff --git a/src/cmd_kv.cc b/src/cmd_kv.cc index abc181773..0885f8405 100644 --- a/src/cmd_kv.cc +++ b/src/cmd_kv.cc @@ -10,7 +10,7 @@ namespace pikiwidb { -GetCmd::GetCmd(const std::string& name, int arity) +GetCmd::GetCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, CmdFlagsReadonly, AclCategoryRead | AclCategoryString) {} bool GetCmd::DoInitial(CmdContext& ctx) { @@ -34,7 +34,7 @@ void GetCmd::DoCmd(CmdContext& ctx) { ctx.AppendString(reply); } -SetCmd::SetCmd(const std::string& name, int arity) +SetCmd::SetCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, CmdFlagsWrite, AclCategoryWrite | AclCategoryString) {} bool SetCmd::DoInitial(CmdContext& ctx) { diff --git a/src/cmd_kv.h b/src/cmd_kv.h index e71e5f608..431a8a152 100644 --- a/src/cmd_kv.h +++ b/src/cmd_kv.h @@ -14,7 +14,7 @@ namespace pikiwidb { class GetCmd : public BaseCmd { public: - GetCmd(const std::string &name, int arity); + GetCmd(const std::string &name, int16_t arity); protected: bool DoInitial(CmdContext &ctx) override; @@ -25,7 +25,7 @@ class GetCmd : public BaseCmd { class SetCmd : public BaseCmd { public: - SetCmd(const std::string &name, int arity); + SetCmd(const std::string &name, int16_t arity); protected: bool DoInitial(CmdContext &ctx) override; diff --git a/src/cmd_table_manager.cc b/src/cmd_table_manager.cc index f6d5a8e48..d96699a2b 100644 --- a/src/cmd_table_manager.cc +++ b/src/cmd_table_manager.cc @@ -21,7 +21,10 @@ void CmdTableManager::InitCmdTable() { std::unique_lock wl(mutex_); // admin - std::unique_ptr configPtr = std::make_unique(kCmdNameConfig, -2); + auto configPtr = std::make_unique(kCmdNameConfig, -2); + configPtr->AddSubCmd(std::make_unique("get", -3)); + configPtr->AddSubCmd(std::make_unique("set", -4)); + cmds_->insert(std::make_pair(kCmdNameConfig, std::move(configPtr))); // kv @@ -31,15 +34,22 @@ void CmdTableManager::InitCmdTable() { cmds_->insert(std::make_pair(kCmdNameSet, std::move(setPtr))); } -BaseCmd* CmdTableManager::GetCommand(const std::string& cmdName) { +std::pair CmdTableManager::GetCommand(const std::string& cmdName, CmdContext& ctx) { std::shared_lock rl(mutex_); auto cmd = cmds_->find(cmdName); if (cmd == cmds_->end()) { - return nullptr; + return std::pair(nullptr, CmdRes::kSyntaxErr); + } + + if (cmd->second->HasSubCommand()) { + if (ctx.argv_.size() < 2) { + return std::pair(nullptr, CmdRes::kInvalidParameter); + } + return std::pair(cmd->second->GetSubCmd(ctx.argv_[1]), CmdRes::kSyntaxErr); } - return cmd->second.get(); + return std::pair(cmd->second.get(), CmdRes::kSyntaxErr); } bool CmdTableManager::CmdExist(const std::string& cmd) const { diff --git a/src/cmd_table_manager.h b/src/cmd_table_manager.h index c9a221d76..2fd1313f3 100644 --- a/src/cmd_table_manager.h +++ b/src/cmd_table_manager.h @@ -28,7 +28,7 @@ class CmdTableManager { public: void InitCmdTable(); - BaseCmd* GetCommand(const std::string& cmdName); + std::pair GetCommand(const std::string& cmdName, CmdContext& ctx); // uint32_t DistributeKey(const std::string& key, uint32_t slot_num); bool CmdExist(const std::string& cmd) const; uint32_t GetCmdId();