blob: e80dcfe117ebe0c0c9de4a319d7ae247bcfe6cc3 [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 <cstdint>
#include "commander.h"
#include "commands/scan_base.h"
#include "error_constants.h"
#include "server/server.h"
#include "types/redis_set.h"
namespace redis {
class CommandSAdd : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
std::vector<Slice> members;
for (unsigned int i = 2; i < args_.size(); i++) {
members.emplace_back(args_[i]);
}
uint64_t ret = 0;
redis::Set set_db(srv->storage, conn->GetNamespace());
auto s = set_db.Add(ctx, args_[1], members, &ret);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = redis::Integer(ret);
return Status::OK();
}
};
class CommandSRem : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
std::vector<Slice> members;
for (size_t i = 2; i < args_.size(); i++) {
members.emplace_back(args_[i]);
}
uint64_t ret = 0;
redis::Set set_db(srv->storage, conn->GetNamespace());
auto s = set_db.Remove(ctx, args_[1], members, &ret);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = redis::Integer(ret);
return Status::OK();
}
};
class CommandSCard : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
redis::Set set_db(srv->storage, conn->GetNamespace());
uint64_t ret = 0;
auto s = set_db.Card(ctx, args_[1], &ret);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = redis::Integer(ret);
return Status::OK();
}
};
class CommandSMembers : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
redis::Set set_db(srv->storage, conn->GetNamespace());
std::vector<std::string> members;
auto s = set_db.Members(ctx, args_[1], &members);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = conn->SetOfBulkStrings(members);
return Status::OK();
}
};
class CommandSIsMember : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
redis::Set set_db(srv->storage, conn->GetNamespace());
bool ret = false;
auto s = set_db.IsMember(ctx, args_[1], args_[2], &ret);
if (!s.ok() && !s.IsNotFound()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = redis::Integer(ret ? 1 : 0);
return Status::OK();
}
};
class CommandSMIsMember : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
redis::Set set_db(srv->storage, conn->GetNamespace());
std::vector<Slice> members;
for (size_t i = 2; i < args_.size(); i++) {
members.emplace_back(args_[i]);
}
std::vector<int> exists;
auto s = set_db.MIsMember(ctx, args_[1], members, &exists);
if (!s.ok() && !s.IsNotFound()) {
return {Status::RedisExecErr, s.ToString()};
}
if (s.IsNotFound()) {
exists.resize(members.size(), 0);
}
output->append(redis::MultiLen(exists.size()));
for (const auto &exist : exists) {
output->append(redis::Integer(exist));
}
return Status::OK();
}
};
class CommandSPop : public Commander {
public:
Status Parse(const std::vector<std::string> &args) override {
if (args.size() > 3) {
return {Status::RedisParseErr, errWrongNumOfArguments};
}
if (args.size() == 3) {
auto parse_result = ParseInt<int>(args[2], 10);
if (!parse_result) {
return {Status::RedisParseErr, errValueNotInteger};
}
if (*parse_result < 0) {
return {Status::RedisParseErr, errValueMustBePositive};
}
count_ = *parse_result;
with_count_ = true;
}
return Commander::Parse(args);
}
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
redis::Set set_db(srv->storage, conn->GetNamespace());
std::vector<std::string> members;
auto s = set_db.Take(ctx, args_[1], &members, count_, true);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
if (with_count_) {
*output = conn->SetOfBulkStrings(members);
} else {
if (members.size() > 0) {
*output = redis::BulkString(members.front());
} else {
*output = conn->NilString();
}
}
return Status::OK();
}
private:
int count_ = 1;
bool with_count_ = false;
};
class CommandSRandMember : public Commander {
public:
Status Parse(const std::vector<std::string> &args) override {
if (args.size() > 3) {
return {Status::RedisParseErr, errWrongNumOfArguments};
}
if (args.size() == 3) {
auto parse_result = ParseInt<int>(args[2], 10);
if (!parse_result) {
return {Status::RedisParseErr, errValueNotInteger};
}
count_ = *parse_result;
}
return Commander::Parse(args);
}
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
redis::Set set_db(srv->storage, conn->GetNamespace());
std::vector<std::string> members;
auto s = set_db.Take(ctx, args_[1], &members, count_, false);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = conn->SetOfBulkStrings(members);
return Status::OK();
}
private:
int count_ = 1;
};
class CommandSMove : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
redis::Set set_db(srv->storage, conn->GetNamespace());
bool ret = false;
auto s = set_db.Move(ctx, args_[1], args_[2], args_[3], &ret);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = redis::Integer(ret ? 1 : 0);
return Status::OK();
}
};
class CommandSDiff : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
std::vector<Slice> keys;
for (size_t i = 1; i < args_.size(); i++) {
keys.emplace_back(args_[i]);
}
std::vector<std::string> members;
redis::Set set_db(srv->storage, conn->GetNamespace());
auto s = set_db.Diff(ctx, keys, &members);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = conn->SetOfBulkStrings(members);
return Status::OK();
}
};
class CommandSUnion : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
std::vector<Slice> keys;
for (size_t i = 1; i < args_.size(); i++) {
keys.emplace_back(args_[i]);
}
std::vector<std::string> members;
redis::Set set_db(srv->storage, conn->GetNamespace());
auto s = set_db.Union(ctx, keys, &members);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = conn->SetOfBulkStrings(members);
return Status::OK();
}
};
class CommandSInter : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
std::vector<Slice> keys;
for (size_t i = 1; i < args_.size(); i++) {
keys.emplace_back(args_[i]);
}
std::vector<std::string> members;
redis::Set set_db(srv->storage, conn->GetNamespace());
auto s = set_db.Inter(ctx, keys, &members);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = conn->SetOfBulkStrings(members);
return Status::OK();
}
};
/*
* description:
* syntax: `SINTERCARD numkeys key [key ...] [LIMIT limit]`
*
* limit: the valid limit is an non-negative integer.
*/
class CommandSInterCard : public Commander {
public:
Status Parse(const std::vector<std::string> &args) override {
auto parse_numkey = ParseInt<int>(args[1], 10);
if (!parse_numkey) {
return {Status::RedisParseErr, errValueNotInteger};
}
if (*parse_numkey <= 0) {
return {Status::RedisParseErr, errValueMustBePositive};
}
numkeys_ = *parse_numkey;
// command: for example, SINTERCARD 2 key1 key2 LIMIT 1
auto arg_sz = args.size();
if (arg_sz == numkeys_ + 4 && util::ToLower(args[numkeys_ + 2]) == "limit") {
auto parse_limit = ParseInt<int>(args[numkeys_ + 3], 10);
if (!parse_limit) {
return {Status::RedisParseErr, errValueNotInteger};
}
if (*parse_limit < 0) {
return {Status::RedisParseErr, errLimitIsNegative};
}
limit_ = *parse_limit;
return Commander::Parse(args);
}
if (arg_sz != numkeys_ + 2) {
return {Status::RedisParseErr, errWrongNumOfArguments};
}
return Commander::Parse(args);
}
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
std::vector<Slice> keys;
for (size_t i = 2; i < numkeys_ + 2; i++) {
keys.emplace_back(args_[i]);
}
redis::Set set_db(srv->storage, conn->GetNamespace());
uint64_t ret = 0;
auto s = set_db.InterCard(ctx, keys, limit_, &ret);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = redis::Integer(ret);
return Status::OK();
}
static CommandKeyRange Range(const std::vector<std::string> &args) {
int num_key = *ParseInt<int>(args[1], 10);
return {2, 1 + num_key, 1};
}
private:
uint64_t numkeys_ = 0;
uint64_t limit_ = 0;
};
class CommandSDiffStore : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
std::vector<Slice> keys;
for (size_t i = 2; i < args_.size(); i++) {
keys.emplace_back(args_[i]);
}
uint64_t ret = 0;
redis::Set set_db(srv->storage, conn->GetNamespace());
auto s = set_db.DiffStore(ctx, args_[1], keys, &ret);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = redis::Integer(ret);
return Status::OK();
}
};
class CommandSUnionStore : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
std::vector<Slice> keys;
for (size_t i = 2; i < args_.size(); i++) {
keys.emplace_back(args_[i]);
}
uint64_t ret = 0;
redis::Set set_db(srv->storage, conn->GetNamespace());
auto s = set_db.UnionStore(ctx, args_[1], keys, &ret);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = redis::Integer(ret);
return Status::OK();
}
};
class CommandSInterStore : public Commander {
public:
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
std::vector<Slice> keys;
for (size_t i = 2; i < args_.size(); i++) {
keys.emplace_back(args_[i]);
}
uint64_t ret = 0;
redis::Set set_db(srv->storage, conn->GetNamespace());
auto s = set_db.InterStore(ctx, args_[1], keys, &ret);
if (!s.ok()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = redis::Integer(ret);
return Status::OK();
}
};
class CommandSScan : public CommandSubkeyScanBase {
public:
CommandSScan() = default;
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
redis::Set set_db(srv->storage, conn->GetNamespace());
std::vector<std::string> members;
auto key_name = srv->GetKeyNameFromCursor(cursor_, CursorType::kTypeSet);
auto s = set_db.Scan(ctx, key_, key_name, limit_, prefix_, &members);
if (!s.ok() && !s.IsNotFound()) {
return {Status::RedisExecErr, s.ToString()};
}
*output = CommandScanBase::GenerateOutput(srv, conn, members, CursorType::kTypeSet);
return Status::OK();
}
};
REDIS_REGISTER_COMMANDS(Set, MakeCmdAttr<CommandSAdd>("sadd", -3, "write", 1, 1, 1),
MakeCmdAttr<CommandSRem>("srem", -3, "write no-dbsize-check", 1, 1, 1),
MakeCmdAttr<CommandSCard>("scard", 2, "read-only", 1, 1, 1),
MakeCmdAttr<CommandSMembers>("smembers", 2, "read-only slow", 1, 1, 1),
MakeCmdAttr<CommandSIsMember>("sismember", 3, "read-only", 1, 1, 1),
MakeCmdAttr<CommandSMIsMember>("smismember", -3, "read-only", 1, 1, 1),
MakeCmdAttr<CommandSPop>("spop", -2, "write", 1, 1, 1),
MakeCmdAttr<CommandSRandMember>("srandmember", -2, "read-only slow", 1, 1, 1),
MakeCmdAttr<CommandSMove>("smove", 4, "write", 1, 2, 1),
MakeCmdAttr<CommandSDiff>("sdiff", -2, "read-only slow", 1, -1, 1),
MakeCmdAttr<CommandSUnion>("sunion", -2, "read-only slow", 1, -1, 1),
MakeCmdAttr<CommandSInter>("sinter", -2, "read-only slow", 1, -1, 1),
MakeCmdAttr<CommandSInterCard>("sintercard", -3, "read-only slow", CommandSInterCard::Range),
MakeCmdAttr<CommandSDiffStore>("sdiffstore", -3, "write slow", 1, -1, 1),
MakeCmdAttr<CommandSUnionStore>("sunionstore", -3, "write slow", 1, -1, 1),
MakeCmdAttr<CommandSInterStore>("sinterstore", -3, "write slow", 1, -1, 1),
MakeCmdAttr<CommandSScan>("sscan", -3, "read-only", 1, 1, 1), )
} // namespace redis