/************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2016 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ //------------------------------------------------------------------------------ //! @author Elvin-Alin Sindrilaru //! @brief Hash map implementation using QClient //------------------------------------------------------------------------------ #include "qclient/structures/QHash.hh" QCLIENT_NAMESPACE_BEGIN //------------------------------------------------------------------------------ // Copy assignment //------------------------------------------------------------------------------ QHash& QHash::operator=(const QHash& other) { mClient = other.mClient; mKey = other.mKey; return *this; } //------------------------------------------------------------------------------ // Copy constructor //------------------------------------------------------------------------------ QHash::QHash(const QHash& other) { *this = other; } //------------------------------------------------------------------------------ // HGET command - synchronous //------------------------------------------------------------------------------ std::string QHash::hget(const std::string& field) { std::string resp{""}; redisReplyPtr reply = mClient->exec("HGET", mKey, field).get(); if ((reply == nullptr) || ((reply->type != REDIS_REPLY_STRING) && (reply->type != REDIS_REPLY_NIL))) { throw std::runtime_error("[FATAL] Error hget key: " + mKey + " field: " + field + ": Unexpected/null reply"); } if (reply->type == REDIS_REPLY_STRING) { resp.append(reply->str, reply->len); } return resp; } //------------------------------------------------------------------------------ // HDEL command - synchronous //------------------------------------------------------------------------------ bool QHash::hdel(const std::string& field) { redisReplyPtr reply = mClient->exec("HDEL", mKey, field).get(); if ((reply == nullptr) || (reply->type != REDIS_REPLY_INTEGER)) { throw std::runtime_error("[FATAL] Error hdel key: " + mKey + " field: " + field + ": Unexpected/null reply"); } return (reply->integer == 1); } //------------------------------------------------------------------------------ // HDEL command - asynchronous //------------------------------------------------------------------------------ void QHash::hdel_async(const std::string& field, AsyncHandler* ah) { ah->Register(mClient, {"HDEL", mKey, field}); } //------------------------------------------------------------------------------ // HGETALL command - synchronous //------------------------------------------------------------------------------ std::vector QHash::hgetall() { redisReplyPtr reply = mClient->exec("HGETALL", mKey).get(); if ((reply == nullptr) || (reply->type != REDIS_REPLY_ARRAY)) { throw std::runtime_error("[FATAL] Error hgetall key: " + mKey + ": Unexpected/null reply"); } std::vector resp; resp.reserve(reply->elements); for (size_t i = 0; i < reply->elements; ++i) { resp.emplace_back(reply->element[i]->str, reply->element[i]->len); } return resp; } //------------------------------------------------------------------------------ // HEXISTS command - synchronous //------------------------------------------------------------------------------ bool QHash::hexists(const std::string& field) { redisReplyPtr reply = mClient->exec("HEXISTS", mKey, field).get(); if (reply->type != REDIS_REPLY_INTEGER) { throw std::runtime_error("[FATAL] Error hexists key: " + mKey + " field: " + field + ": Unexpected/null reply"); } return (reply->integer == 1); } //------------------------------------------------------------------------------ // HLEN command - synchronous //------------------------------------------------------------------------------ long long int QHash::hlen() { redisReplyPtr reply = mClient->exec("HLEN", mKey).get(); if (reply->type != REDIS_REPLY_INTEGER) { throw std::runtime_error("[FATAL] Error hlen key: " + mKey + ": Unexpected/null reply"); } return reply->integer; } //------------------------------------------------------------------------------ // HLEN command - asynchronous //------------------------------------------------------------------------------ void QHash::hlen_async(AsyncHandler* ah) { ah->Register(mClient, {"HLEN", mKey}); } //------------------------------------------------------------------------------ // HKEYS command - synchronous //------------------------------------------------------------------------------ std::vector QHash::hkeys() { redisReplyPtr reply = mClient->exec("HKEYS", mKey).get(); if ((reply == nullptr) || (reply->type != REDIS_REPLY_ARRAY)) { throw std::runtime_error("[FATAL] Error hkeys key: " + mKey + ": Unexpected/null reply"); } std::vector resp; resp.reserve(reply->elements); for (size_t i = 0; i < reply->elements; ++i) { resp.emplace_back(reply->element[i]->str, reply->element[i]->len); } return resp; } //------------------------------------------------------------------------------ // HVALS command - synchronous //------------------------------------------------------------------------------ std::vector QHash::hvals() { redisReplyPtr reply = mClient->exec("HVALS", mKey).get(); if ((reply == nullptr) || (reply->type != REDIS_REPLY_ARRAY)) { throw std::runtime_error("[FATAL] Error hvals key: " + mKey + ": Unexpected/null reply"); } std::vector resp; resp.reserve(reply->elements); for (size_t i = 0; i < reply->elements; ++i) { resp.emplace_back(reply->element[i]->str, reply->element[i]->len); } return resp; } //------------------------------------------------------------------------------ // HSCAN command - synchronous //------------------------------------------------------------------------------ std::pair > QHash::hscan(const std::string& cursor, long long count) { redisReplyPtr reply = mClient->exec("HSCAN", mKey, cursor, "COUNT", std::to_string(count)).get(); if (reply == nullptr) { throw std::runtime_error("[FATAL] Error hscan key: " + mKey + ": Unexpected/null reply"); } // Parse the Redis reply std::string new_cursor = std::string(reply->element[0]->str, reply->element[0]->len); // First element is the new cursor std::pair > retc_pair; retc_pair.first = new_cursor; // Get array part of the response redisReply* array = reply->element[1]; for (unsigned long i = 0; i < array->elements; i += 2) { retc_pair.second.emplace( std::string(array->element[i]->str, static_cast(array->element[i]->len)), std::string(array->element[i + 1]->str, static_cast(array->element[i + 1]->len))); } return retc_pair; } //------------------------------------------------------------------------------ // HASH multi set command - synchronous //------------------------------------------------------------------------------ bool QHash::hmset(std::list lst_elem) { (void) lst_elem.push_front(mKey); (void) lst_elem.push_front("HMSET"); redisReplyPtr reply = mClient->execute(lst_elem).get(); if ((reply == nullptr) || (reply->type != REDIS_REPLY_STATUS)) { throw std::runtime_error("[FATAL] Error hmset key: " + mKey + " with multiple members: Unexpected/null reply type"); } return true; } //------------------------------------------------------------------------------ // HASH Get iterator //------------------------------------------------------------------------------ QHash::Iterator QHash::getIterator(size_t count, const std::string &startCursor) { return QHash::Iterator(*this, count, startCursor); } //------------------------------------------------------------------------------ // HASH Iterator implementation //------------------------------------------------------------------------------ QHash::Iterator::Iterator(QHash &qh, size_t cnt, const std::string &crs) : qhash(qh), count(cnt), cursor(crs) { fillFromBackend(); } bool QHash::Iterator::valid() const { return ! results.empty(); } void QHash::Iterator::fillFromBackend() { while(!reachedEnd && results.empty()) { reqs++; std::pair > answer = qhash.hscan(cursor, count); cursor = answer.first; results = std::move(answer.second); if(cursor == "0") { reachedEnd = true; } } } void QHash::Iterator::next() { if(!results.empty()) { results.erase(results.begin()); } fillFromBackend(); } std::string QHash::Iterator::getKey() const { return results.begin()->first; } std::string QHash::Iterator::getValue() const { return results.begin()->second; } size_t QHash::Iterator::requestsSoFar() const { return reqs; } QCLIENT_NAMESPACE_END