/* * Copyright (C) 2019 pengjian.uestc @ gmail.com */ /* * SPDX-License-Identifier: AGPL-3.0-or-later */ #include "redis/commands.hh" #include #include "redis/request.hh" #include "redis/reply.hh" #include "types.hh" #include "service_permit.hh" #include "service/client_state.hh" #include "redis/options.hh" #include "redis/query_utils.hh" #include "redis/mutation_utils.hh" #include "redis/lolwut.hh" #include "redis/keyspace_utils.hh" namespace redis { namespace commands { future get(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() != 1) { throw wrong_arguments_exception(1, req.arguments_size(), req._command); } return redis::read_strings(proxy, options, req._args[0], permit).then([] (auto result) { if (result->has_result()) { return redis_message::make_strings_result(std::move(result->result())); } // return nil string if key does not exist return redis_message::nil(); }); } future exists(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() < 1) { throw wrong_arguments_exception(1, req.arguments_size(), req._command); } return do_with(size_t(0), [&proxy, &options, permit, &req] (size_t& count) { return seastar::do_for_each(req._args, [&proxy, &options, permit, &count] (auto& key) { return redis::read_strings(proxy, options, key, permit).then([&count] (lw_shared_ptr result) { if (result->has_result()) { count++; } }); }).then([&count] () { return redis_message::number(count); }); }); } future ttl(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() != 1) { throw wrong_arguments_exception(1, req.arguments_size(), req._command); } return redis::read_strings(proxy, options, req._args[0], permit).then([] (auto result) { if (result->has_result()) { if (result->has_ttl()) { return redis_message::number(result->ttl().count()); }else{ return redis_message::number(-1); } } return redis_message::number(-2); }); } future strlen(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() != 1) { throw wrong_arguments_exception(1, req.arguments_size(), req._command); } return redis::read_strings(proxy, options, req._args[0], permit).then([] (auto result) { if (result->has_result()) { return redis_message::number(result->result().length()); } // return 0 string if key does not exist return redis_message::zero(); }); } future hgetall(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() != 1) { throw wrong_number_of_arguments_exception(req._command); } return redis::read_hashes(proxy, options, req._args[0], permit).then([] (auto result) { if (!result->empty()) { return redis_message::make_list_result(*result); } // return nil string if key does not exist return redis_message::nil(); }); } future hget(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() != 2) { throw wrong_arguments_exception(2, req.arguments_size(), req._command); } auto field = std::move(req._args[1]); return redis::read_hashes(proxy, options, req._args[0], field, permit).then([field] (auto result) { if (!result->empty()) { return redis_message::make_strings_result(std::move(result->at(field))); } // return nil string if key does not exist return redis_message::nil(); }); } future hset(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() != 3) { throw wrong_number_of_arguments_exception(req._command); } return redis::write_hashes(proxy, options, std::move(req._args[0]), std::move(req._args[1]), std::move(req._args[2]), 0, permit).then([] { return redis_message::one(); }); } future hdel(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() < 2) { throw wrong_number_of_arguments_exception(req._command); } //FIXME: We should return the count of the actually deleted fields. auto fields = std::vector(req._args.begin() + 1, req._args.end()); auto size = fields.size(); return redis::delete_fields(proxy, options, std::move(req._args[0]), std::move(fields), permit).then([size] { return redis_message::number(size); }); } future hexists(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() != 2) { throw wrong_arguments_exception(2, req.arguments_size(), req._command); } return redis::read_hashes(proxy, options, req._args[0], req._args[1], permit).then([] (auto result) { return redis_message::number(result->empty() ? 0 : 1); }); } future set(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() != 2 && req.arguments_size() != 4) { throw invalid_arguments_exception(req._command); } long ttl = 0; if (req.arguments_size() == 4) { bytes opt; opt.resize(req._args[2].size()); std::transform(req._args[2].begin(), req._args[2].end(), opt.begin(), ::tolower); if (opt == "ex") { try { ttl = std::stol(std::string(reinterpret_cast(req._args[3].data()), req._args[3].size())); } catch (...) { throw invalid_arguments_exception(req._command); } } } return redis::write_strings(proxy, options, std::move(req._args[0]), std::move(req._args[1]), ttl, permit).then([] { return redis_message::ok(); }); } future setex(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() != 3) { throw wrong_arguments_exception(3, req.arguments_size(), req._command); } long ttl = std::stol(std::string(reinterpret_cast(req._args[1].data()), req._args[1].size())); return redis::write_strings(proxy, options, std::move(req._args[0]), std::move(req._args[2]), ttl, permit).then([] { return redis_message::ok(); }); } future del(service::storage_proxy& proxy, request& req, redis::redis_options& options, service_permit permit) { if (req.arguments_size() == 0) { throw wrong_number_of_arguments_exception(req._command); } //FIXME: We should return the count of the actually deleted keys. auto keys = req._args; auto size = keys.size(); return redis::delete_objects(proxy, options, std::move(keys), permit).then([size] { return redis_message::number(size); }); } future select(service::storage_proxy&, request& req, redis::redis_options& options, service_permit) { if (req.arguments_size() != 1) { throw wrong_arguments_exception(1, req.arguments_size(), req._command); } long index = -1; try { index = std::stol(std::string(reinterpret_cast(req._args[0].data()), req._args[0].size())); } catch (...) { throw invalid_db_index_exception(); } if (index < 0 || static_cast(index) >= options.get_total_redis_db_count()) { throw invalid_db_index_exception(); } options.set_keyspace_name(fmt::format("REDIS_{}", static_cast(index))); return redis_message::ok(); } future unknown(service::storage_proxy&, request& req, redis::redis_options&, service_permit) { return redis_message::unknown(std::move(req._command)); } future ping(service::storage_proxy&, request& req, redis::redis_options&, service_permit) { return redis_message::pong(); } future echo(service::storage_proxy&, request& req, redis::redis_options&, service_permit) { if (req.arguments_size() != 1) { throw wrong_arguments_exception(1, req.arguments_size(), req._command); } return redis_message::make_strings_result(req._args[0]); } future lolwut(service::storage_proxy&, request& req, redis::redis_options& options, service_permit) { int cols = 66; int squares_per_row = 8; int squares_per_col = 12; try { if (req.arguments_size() >= 1) { cols = std::stoi(std::string(reinterpret_cast(req._args[0].data()), req._args[0].size())); cols = std::clamp(cols, 1, 1000); } if (req.arguments_size() >= 2) { squares_per_row = std::stoi(std::string(reinterpret_cast(req._args[1].data()), req._args[1].size())); squares_per_row = std::clamp(squares_per_row, 1, 200); } if (req.arguments_size() >= 3) { squares_per_col = std::stoi(std::string(reinterpret_cast(req._args[2].data()), req._args[2].size())); squares_per_col = std::clamp(squares_per_col, 1, 200); } } catch (...) { throw wrong_arguments_exception(1, req.arguments_size(), req._command); } return redis::lolwut5(cols, squares_per_row, squares_per_col).then([] (auto result) { return redis_message::make_strings_result(std::move(result)); }); } } }