blob: d820d3fb8511db5d15ed52d6e0691470c0287839 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#include "commander.h"
#include "cluster/cluster_defs.h"
#include "server/redis_reply.h"
namespace redis {
RegisterToCommandTable::RegisterToCommandTable(CommandCategory category,
std::initializer_list<CommandAttributes> list) {
if (category == CommandCategory::Disabled) {
return;
}
for (auto attr : list) {
attr.category = category;
CommandTable::redis_command_table.emplace_back(attr);
CommandTable::original_commands[attr.name] = &CommandTable::redis_command_table.back();
CommandTable::commands[attr.name] = &CommandTable::redis_command_table.back();
}
}
size_t CommandTable::Size() { return redis_command_table.size(); }
const CommandMap *CommandTable::GetOriginal() { return &original_commands; }
CommandMap *CommandTable::Get() { return &commands; }
void CommandTable::Reset() { commands = original_commands; }
std::string CommandTable::GetCommandInfo(const CommandAttributes *command_attributes) {
std::string command, command_flags;
command.append(redis::MultiLen(6));
command.append(redis::BulkString(command_attributes->name));
command.append(redis::Integer(command_attributes->arity));
command.append(redis::ArrayOfBulkStrings(CommandAttributes::FlagsToString(command_attributes->InitialFlags())));
auto key_range = command_attributes->InitialKeyRange().ValueOr({0, 0, 0});
command.append(redis::Integer(key_range.first_key));
command.append(redis::Integer(key_range.last_key));
command.append(redis::Integer(key_range.key_step));
return command;
}
void CommandTable::GetAllCommandsInfo(std::string *info) {
info->append(redis::MultiLen(commands.size()));
for (const auto &iter : commands) {
auto command_attribute = iter.second;
auto command_info = GetCommandInfo(command_attribute);
info->append(command_info);
}
}
void CommandTable::GetCommandsInfo(std::string *info, const std::vector<std::string> &cmd_names) {
info->append(redis::MultiLen(cmd_names.size()));
for (const auto &cmd_name : cmd_names) {
auto cmd_iter = commands.find(util::ToLower(cmd_name));
if (cmd_iter == commands.end()) {
info->append(NilString(RESP::v2));
} else {
auto command_attribute = cmd_iter->second;
auto command_info = GetCommandInfo(command_attribute);
info->append(command_info);
}
}
}
StatusOr<std::vector<int>> CommandTable::GetKeysFromCommand(const CommandAttributes *attributes,
const std::vector<std::string> &cmd_tokens) {
int argc = static_cast<int>(cmd_tokens.size());
if (!attributes->CheckArity(argc)) {
return {Status::NotOK, "Invalid number of arguments specified for command"};
}
auto cmd = attributes->factory();
if (auto s = cmd->Parse(cmd_tokens); !s) {
return {Status::NotOK, "Invalid syntax found in this command arguments: " + s.Msg()};
}
Status status;
std::vector<int> key_indexes;
attributes->ForEachKeyRange(
[&](const std::vector<std::string> &, CommandKeyRange key_range) {
key_range.ForEachKeyIndex([&](int i) { key_indexes.push_back(i); }, cmd_tokens.size());
},
cmd_tokens, [&](const auto &) { status = {Status::NotOK, "The command has no key arguments"}; });
if (!status) {
return status;
}
return key_indexes;
}
bool CommandTable::IsExists(const std::string &name) {
return original_commands.find(util::ToLower(name)) != original_commands.end();
}
Status CommandTable::ParseSlotRanges(const std::string &slots_str, std::vector<SlotRange> &slots) {
if (slots_str.empty()) {
return {Status::NotOK, "No slots to parse."};
}
std::vector<std::string> slot_ranges = util::Split(slots_str, " ");
if (slot_ranges.empty()) {
return {Status::NotOK,
fmt::format("Invalid slots: `{}`. No slots to parse. Please use spaces to separate slots.", slots_str)};
}
auto valid_range = NumericRange<int>{0, kClusterSlots - 1};
// Parse all slots (include slot ranges)
for (auto &slot_range : slot_ranges) {
if (slot_range.find('-') == std::string::npos) {
auto parse_result = ParseInt<int>(slot_range, valid_range, 10);
if (!parse_result) {
return std::move(parse_result).Prefixed(errInvalidSlotID);
}
slots.emplace_back(*parse_result, *parse_result);
continue;
}
// parse slot range: "int1-int2" (satisfy: int1 <= int2 )
if (slot_range.front() == '-' || slot_range.back() == '-') {
return {Status::NotOK,
fmt::format("Invalid slot range: `{}`. The character '-' can't appear in the first or last position.",
slot_range)};
}
std::vector<std::string> fields = util::Split(slot_range, "-");
if (fields.size() != 2) {
return {Status::NotOK,
fmt::format("Invalid slot range: `{}`. The slot range should be of the form `int1-int2`.", slot_range)};
}
auto parse_start = ParseInt<int>(fields[0], valid_range, 10);
auto parse_end = ParseInt<int>(fields[1], valid_range, 10);
if (!parse_start || !parse_end || *parse_start > *parse_end) {
return {Status::NotOK,
fmt::format(
"Invalid slot range: `{}`. The slot range `int1-int2` needs to satisfy the condition (int1 <= int2).",
slot_range)};
}
slots.emplace_back(*parse_start, *parse_end);
}
return Status::OK();
}
} // namespace redis