From 5babd01d407b45d70d31291fec18442d78fe7717 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 12 May 2020 12:58:09 -0700 Subject: [PATCH] centos8 binaries for libhiredis and libredis++ --- ext/hiredis-0.14.1/lib/centos8/libhiredis.a | Bin 0 -> 485314 bytes .../centos8/include/sw/redis++/command.h | 2233 +++++++++++++++++ .../centos8/include/sw/redis++/command_args.h | 180 ++ .../include/sw/redis++/command_options.h | 211 ++ .../centos8/include/sw/redis++/connection.h | 194 ++ .../include/sw/redis++/connection_pool.h | 115 + .../centos8/include/sw/redis++/errors.h | 159 ++ .../centos8/include/sw/redis++/pipeline.h | 49 + .../centos8/include/sw/redis++/queued_redis.h | 1844 ++++++++++++++ .../include/sw/redis++/queued_redis.hpp | 208 ++ .../centos8/include/sw/redis++/redis++.h | 25 + .../centos8/include/sw/redis++/redis.h | 1523 +++++++++++ .../centos8/include/sw/redis++/redis.hpp | 1365 ++++++++++ .../include/sw/redis++/redis_cluster.h | 1395 ++++++++++ .../include/sw/redis++/redis_cluster.hpp | 1415 +++++++++++ .../centos8/include/sw/redis++/reply.h | 363 +++ .../centos8/include/sw/redis++/sentinel.h | 138 + .../centos8/include/sw/redis++/shards.h | 115 + .../centos8/include/sw/redis++/shards_pool.h | 137 + .../centos8/include/sw/redis++/subscriber.h | 231 ++ .../centos8/include/sw/redis++/transaction.h | 77 + .../centos8/include/sw/redis++/utils.h | 269 ++ 22 files changed, 12246 insertions(+) create mode 100644 ext/hiredis-0.14.1/lib/centos8/libhiredis.a create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_args.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_options.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection_pool.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/errors.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/pipeline.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.hpp create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis++.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.hpp create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.hpp create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/reply.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/sentinel.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards_pool.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/subscriber.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/transaction.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/utils.h diff --git a/ext/hiredis-0.14.1/lib/centos8/libhiredis.a b/ext/hiredis-0.14.1/lib/centos8/libhiredis.a new file mode 100644 index 0000000000000000000000000000000000000000..0b9638798529747795a78056068d5a6a980a4f02 GIT binary patch literal 485314 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 z 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 `{Mqlv@RKR^@z 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
Q%T2_I%e7COCjOED#eM!6tA( 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 P0(u6)eTXtRiLM_2G?G}3 literal 0 HcmV?d00001 diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command.h new file mode 100644 index 000000000..3a4b24c5e --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command.h @@ -0,0 +1,2233 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_H + +#include +#include +#include +#include +#include "connection.h" +#include "command_options.h" +#include "command_args.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace cmd { + +// CONNECTION command. +inline void auth(Connection &connection, const StringView &password) { + connection.send("AUTH %b", password.data(), password.size()); +} + +inline void echo(Connection &connection, const StringView &msg) { + connection.send("ECHO %b", msg.data(), msg.size()); +} + +inline void ping(Connection &connection) { + connection.send("PING"); +} + +inline void quit(Connection &connection) { + connection.send("QUIT"); +} + +inline void ping(Connection &connection, const StringView &msg) { + // If *msg* is empty, Redis returns am empty reply of REDIS_REPLY_STRING type. + connection.send("PING %b", msg.data(), msg.size()); +} + +inline void select(Connection &connection, long long idx) { + connection.send("SELECT %lld", idx); +} + +inline void swapdb(Connection &connection, long long idx1, long long idx2) { + connection.send("SWAPDB %lld %lld", idx1, idx2); +} + +// SERVER commands. + +inline void bgrewriteaof(Connection &connection) { + connection.send("BGREWRITEAOF"); +} + +inline void bgsave(Connection &connection) { + connection.send("BGSAVE"); +} + +inline void dbsize(Connection &connection) { + connection.send("DBSIZE"); +} + +inline void flushall(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHALL ASYNC"); + } else { + connection.send("FLUSHALL"); + } +} + +inline void flushdb(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHDB ASYNC"); + } else { + connection.send("FLUSHDB"); + } +} + +inline void info(Connection &connection) { + connection.send("INFO"); +} + +inline void info(Connection &connection, const StringView §ion) { + connection.send("INFO %b", section.data(), section.size()); +} + +inline void lastsave(Connection &connection) { + connection.send("LASTSAVE"); +} + +inline void save(Connection &connection) { + connection.send("SAVE"); +} + +// KEY commands. + +inline void del(Connection &connection, const StringView &key) { + connection.send("DEL %b", key.data(), key.size()); +} + +template +inline void del_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "DEL" << std::make_pair(first, last); + + connection.send(args); +} + +inline void dump(Connection &connection, const StringView &key) { + connection.send("DUMP %b", key.data(), key.size()); +} + +inline void exists(Connection &connection, const StringView &key) { + connection.send("EXISTS %b", key.data(), key.size()); +} + +template +inline void exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void expire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("EXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void expireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("EXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void keys(Connection &connection, const StringView &pattern) { + connection.send("KEYS %b", pattern.data(), pattern.size()); +} + +inline void move(Connection &connection, const StringView &key, long long db) { + connection.send("MOVE %b %lld", + key.data(), key.size(), + db); +} + +inline void persist(Connection &connection, const StringView &key) { + connection.send("PERSIST %b", key.data(), key.size()); +} + +inline void pexpire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("PEXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void pexpireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("PEXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void pttl(Connection &connection, const StringView &key) { + connection.send("PTTL %b", key.data(), key.size()); +} + +inline void randomkey(Connection &connection) { + connection.send("RANDOMKEY"); +} + +inline void rename(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAME %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +inline void renamenx(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAMENX %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +void restore(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + bool replace); + +inline void scan(Connection &connection, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SCAN %lld MATCH %b COUNT %lld", + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void touch(Connection &connection, const StringView &key) { + connection.send("TOUCH %b", key.data(), key.size()); +} + +template +inline void touch_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "TOUCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void ttl(Connection &connection, const StringView &key) { + connection.send("TTL %b", key.data(), key.size()); +} + +inline void type(Connection &connection, const StringView &key) { + connection.send("TYPE %b", key.data(), key.size()); +} + +inline void unlink(Connection &connection, const StringView &key) { + connection.send("UNLINK %b", key.data(), key.size()); +} + +template +inline void unlink_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "UNLINK" << std::make_pair(first, last); + + connection.send(args); +} + +inline void wait(Connection &connection, long long numslave, long long timeout) { + connection.send("WAIT %lld %lld", numslave, timeout); +} + +// STRING commands. + +inline void append(Connection &connection, const StringView &key, const StringView &str) { + connection.send("APPEND %b %b", + key.data(), key.size(), + str.data(), str.size()); +} + +inline void bitcount(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("BITCOUNT %b %lld %lld", + key.data(), key.size(), + start, end); +} + +void bitop(Connection &connection, + BitOp op, + const StringView &destination, + const StringView &key); + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last); + +inline void bitpos(Connection &connection, + const StringView &key, + long long bit, + long long start, + long long end) { + connection.send("BITPOS %b %lld %lld %lld", + key.data(), key.size(), + bit, + start, + end); +} + +inline void decr(Connection &connection, const StringView &key) { + connection.send("DECR %b", key.data(), key.size()); +} + +inline void decrby(Connection &connection, const StringView &key, long long decrement) { + connection.send("DECRBY %b %lld", + key.data(), key.size(), + decrement); +} + +inline void get(Connection &connection, const StringView &key) { + connection.send("GET %b", + key.data(), key.size()); +} + +inline void getbit(Connection &connection, const StringView &key, long long offset) { + connection.send("GETBIT %b %lld", + key.data(), key.size(), + offset); +} + +inline void getrange(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("GETRANGE %b %lld %lld", + key.data(), key.size(), + start, + end); +} + +inline void getset(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("GETSET %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void incr(Connection &connection, const StringView &key) { + connection.send("INCR %b", key.data(), key.size()); +} + +inline void incrby(Connection &connection, const StringView &key, long long increment) { + connection.send("INCRBY %b %lld", + key.data(), key.size(), + increment); +} + +inline void incrbyfloat(Connection &connection, const StringView &key, double increment) { + connection.send("INCRBYFLOAT %b %f", + key.data(), key.size(), + increment); +} + +template +inline void mget(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MGET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void mset(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void msetnx(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSETNX" << std::make_pair(first, last); + + connection.send(args); +} + +inline void psetex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("PSETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +void set(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + UpdateType type); + +inline void setex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("SETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +inline void setnx(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("SETNX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void setrange(Connection &connection, + const StringView &key, + long long offset, + const StringView &val) { + connection.send("SETRANGE %b %lld %b", + key.data(), key.size(), + offset, + val.data(), val.size()); +} + +inline void strlen(Connection &connection, const StringView &key) { + connection.send("STRLEN %b", key.data(), key.size()); +} + +// LIST commands. + +inline void blpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BLPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void blpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BLPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BRPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void brpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BRPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpoplpush(Connection &connection, + const StringView &source, + const StringView &destination, + long long timeout) { + connection.send("BRPOPLPUSH %b %b %lld", + source.data(), source.size(), + destination.data(), destination.size(), + timeout); +} + +inline void lindex(Connection &connection, const StringView &key, long long index) { + connection.send("LINDEX %b %lld", + key.data(), key.size(), + index); +} + +void linsert(Connection &connection, + const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + +inline void llen(Connection &connection, + const StringView &key) { + connection.send("LLEN %b", key.data(), key.size()); +} + +inline void lpop(Connection &connection, const StringView &key) { + connection.send("LPOP %b", + key.data(), key.size()); +} + +inline void lpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void lpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "LPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void lpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void lrange(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void lrem(Connection &connection, + const StringView &key, + long long count, + const StringView &val) { + connection.send("LREM %b %lld %b", + key.data(), key.size(), + count, + val.data(), val.size()); +} + +inline void lset(Connection &connection, + const StringView &key, + long long index, + const StringView &val) { + connection.send("LSET %b %lld %b", + key.data(), key.size(), + index, + val.data(), val.size()); +} + +inline void ltrim(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LTRIM %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void rpop(Connection &connection, const StringView &key) { + connection.send("RPOP %b", key.data(), key.size()); +} + +inline void rpoplpush(Connection &connection, + const StringView &source, + const StringView &destination) { + connection.send("RPOPLPUSH %b %b", + source.data(), source.size(), + destination.data(), destination.size()); +} + +inline void rpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void rpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "RPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void rpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +// HASH commands. + +inline void hdel(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HDEL %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +template +inline void hdel_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hexists(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HEXISTS %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hget(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HGET %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hgetall(Connection &connection, const StringView &key) { + connection.send("HGETALL %b", key.data(), key.size()); +} + +inline void hincrby(Connection &connection, + const StringView &key, + const StringView &field, + long long increment) { + connection.send("HINCRBY %b %b %lld", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hincrbyfloat(Connection &connection, + const StringView &key, + const StringView &field, + double increment) { + connection.send("HINCRBYFLOAT %b %b %f", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hkeys(Connection &connection, const StringView &key) { + connection.send("HKEYS %b", key.data(), key.size()); +} + +inline void hlen(Connection &connection, const StringView &key) { + connection.send("HLEN %b", key.data(), key.size()); +} + +template +inline void hmget(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMGET" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void hmset(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMSET" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("HSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void hset(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSET %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +inline void hsetnx(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSETNX %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +inline void hstrlen(Connection &connection, + const StringView &key, + const StringView &field) { + connection.send("HSTRLEN %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hvals(Connection &connection, const StringView &key) { + connection.send("HVALS %b", key.data(), key.size()); +} + +// SET commands + +inline void sadd(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SADD %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void sadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void scard(Connection &connection, const StringView &key) { + connection.send("SCARD %b", key.data(), key.size()); +} + +template +inline void sdiff(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFF" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sdiffstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SDIFFSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sdiffstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFFSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void sinter(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTER" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sinterstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SINTERSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTERSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +inline void sismember(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SISMEMBER %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void smembers(Connection &connection, const StringView &key) { + connection.send("SMEMBERS %b", key.data(), key.size()); +} + +inline void smove(Connection &connection, + const StringView &source, + const StringView &destination, + const StringView &member) { + connection.send("SMOVE %b %b %b", + source.data(), source.size(), + destination.data(), destination.size(), + member.data(), member.size()); +} + +inline void spop(Connection &connection, const StringView &key) { + connection.send("SPOP %b", key.data(), key.size()); +} + +inline void spop_range(Connection &connection, const StringView &key, long long count) { + connection.send("SPOP %b %lld", + key.data(), key.size(), + count); +} + +inline void srandmember(Connection &connection, const StringView &key) { + connection.send("SRANDMEMBER %b", key.data(), key.size()); +} + +inline void srandmember_range(Connection &connection, + const StringView &key, + long long count) { + connection.send("SRANDMEMBER %b %lld", + key.data(), key.size(), + count); +} + +inline void srem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void srem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void sscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +template +inline void sunion(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNION" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sunionstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SUNIONSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNIONSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// Sorted Set commands. + +inline void bzpopmax(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMAX %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmax_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMAX" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void bzpopmin(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMIN %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmin_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMIN" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed); + +inline void zadd(Connection &connection, + const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + auto tmp = {std::make_pair(member, score)}; + + zadd_range(connection, key, tmp.begin(), tmp.end(), type, changed); +} + +inline void zcard(Connection &connection, const StringView &key) { + connection.send("ZCARD %b", key.data(), key.size()); +} + +template +inline void zcount(Connection &connection, + const StringView &key, + const Interval &interval) { + connection.send("ZCOUNT %b %s %s", + key.data(), key.size(), + interval.min().c_str(), + interval.max().c_str()); +} + +inline void zincrby(Connection &connection, + const StringView &key, + double increment, + const StringView &member) { + connection.send("ZINCRBY %b %f %b", + key.data(), key.size(), + increment, + member.data(), member.size()); +} + +inline void zinterstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZINTERSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +template +inline void zlexcount(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZLEXCOUNT %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zpopmax(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMAX %b %lld", + key.data(), key.size(), + count); +} + +inline void zpopmin(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMIN %b %lld", + key.data(), key.size(), + count); +} + +inline void zrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +template +void zrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } else { + connection.send("ZRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } +} + +inline void zrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zrem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void zrem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "ZREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void zremrangebylex(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYLEX %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zremrangebyrank(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("zremrangebyrank %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +template +inline void zremrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYSCORE %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zrevrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZREVRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZREVRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrevrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREVRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); +} + +template +void zrevrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZREVRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } else { + connection.send("ZREVRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } +} + +inline void zrevrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREVRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("ZSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void zscore(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZSCORE %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zunionstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZUNIONSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +// HYPERLOGLOG commands. + +inline void pfadd(Connection &connection, + const StringView &key, + const StringView &element) { + connection.send("PFADD %b %b", + key.data(), key.size(), + element.data(), element.size()); +} + +template +inline void pfadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfcount(Connection &connection, const StringView &key) { + connection.send("PFCOUNT %b", key.data(), key.size()); +} + +template +inline void pfcount_range(Connection &connection, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFCOUNT" << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfmerge(Connection &connection, const StringView &destination, const StringView &key) { + connection.send("PFMERGE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void pfmerge_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFMERGE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// GEO commands. + +inline void geoadd(Connection &connection, + const StringView &key, + const std::tuple &member) { + const auto &mem = std::get<0>(member); + + connection.send("GEOADD %b %f %f %b", + key.data(), key.size(), + std::get<1>(member), + std::get<2>(member), + mem.data(), mem.size()); +} + +template +inline void geoadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOADD" << key; + + while (first != last) { + const auto &member = *first; + args << std::get<1>(member) << std::get<2>(member) << std::get<0>(member); + ++first; + } + + connection.send(args); +} + +void geodist(Connection &connection, + const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit); + +template +inline void geohash_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOHASH" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void geopos_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOPOS" << key << std::make_pair(first, last); + + connection.send(args); +} + +void georadius(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadius_store(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void georadiusbymember(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadiusbymember_store(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +// SCRIPTING commands. + +inline void eval(Connection &connection, + const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + CmdArgs cmd_args; + + cmd_args << "EVAL" << script << keys.size() + << std::make_pair(keys.begin(), keys.end()) + << std::make_pair(args.begin(), args.end()); + + connection.send(cmd_args); +} + +inline void evalsha(Connection &connection, + const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + CmdArgs cmd_args; + + cmd_args << "EVALSHA" << script << keys.size() + << std::make_pair(keys.begin(), keys.end()) + << std::make_pair(args.begin(), args.end()); + + connection.send(cmd_args); +} + +inline void script_exists(Connection &connection, const StringView &sha) { + connection.send("SCRIPT EXISTS %b", sha.data(), sha.size()); +} + +template +inline void script_exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SCRIPT" << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void script_flush(Connection &connection) { + connection.send("SCRIPT FLUSH"); +} + +inline void script_kill(Connection &connection) { + connection.send("SCRIPT KILL"); +} + +inline void script_load(Connection &connection, const StringView &script) { + connection.send("SCRIPT LOAD %b", script.data(), script.size()); +} + +// PUBSUB commands. + +inline void psubscribe(Connection &connection, const StringView &pattern) { + connection.send("PSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void psubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void publish(Connection &connection, + const StringView &channel, + const StringView &message) { + connection.send("PUBLISH %b %b", + channel.data(), channel.size(), + message.data(), message.size()); +} + +inline void punsubscribe(Connection &connection) { + connection.send("PUNSUBSCRIBE"); +} + +inline void punsubscribe(Connection &connection, const StringView &pattern) { + connection.send("PUNSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void punsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PUNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PUNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void subscribe(Connection &connection, const StringView &channel) { + connection.send("SUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void subscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("SUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "SUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void unsubscribe(Connection &connection) { + connection.send("UNSUBSCRIBE"); +} + +inline void unsubscribe(Connection &connection, const StringView &channel) { + connection.send("UNSUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void unsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "UNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +// Transaction commands. + +inline void discard(Connection &connection) { + connection.send("DISCARD"); +} + +inline void exec(Connection &connection) { + connection.send("EXEC"); +} + +inline void multi(Connection &connection) { + connection.send("MULTI"); +} + +inline void unwatch(Connection &connection, const StringView &key) { + connection.send("UNWATCH %b", key.data(), key.size()); +} + +template +inline void unwatch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNWATCH: no key specified"); + } + + CmdArgs args; + args << "UNWATCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void watch(Connection &connection, const StringView &key) { + connection.send("WATCH %b", key.data(), key.size()); +} + +template +inline void watch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("WATCH: no key specified"); + } + + CmdArgs args; + args << "WATCH" << std::make_pair(first, last); + + connection.send(args); +} + +// Stream commands. + +inline void xack(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XACK %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +template +void xack_range(Connection &connection, + const StringView &key, + const StringView &group, + Input first, + Input last) { + CmdArgs args; + args << "XACK" << key << group << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last) { + CmdArgs args; + args << "XADD" << key << id << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_maxlen_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + CmdArgs args; + args << "XADD" << key << "MAXLEN"; + + if (approx) { + args << "~"; + } + + args << count << id << std::make_pair(first, last); + + connection.send(args); +} + +inline void xclaim(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + const StringView &id) { + connection.send("XCLAIM %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size(), + min_idle_time, + id.data(), id.size()); +} + +template +void xclaim_range(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + Input first, + Input last) { + CmdArgs args; + args << "XCLAIM" << key << group << consumer << min_idle_time << std::make_pair(first, last); + + connection.send(args); +} + +inline void xdel(Connection &connection, const StringView &key, const StringView &id) { + connection.send("XDEL %b %b", key.data(), key.size(), id.data(), id.size()); +} + +template +void xdel_range(Connection &connection, const StringView &key, Input first, Input last) { + CmdArgs args; + args << "XDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void xgroup_create(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream) { + CmdArgs args; + args << "XGROUP" << "CREATE" << key << group << id; + + if (mkstream) { + args << "MKSTREAM"; + } + + connection.send(args); +} + +inline void xgroup_setid(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XGROUP SETID %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +inline void xgroup_destroy(Connection &connection, + const StringView &key, + const StringView &group) { + connection.send("XGROUP DESTROY %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xgroup_delconsumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer) { + connection.send("XGROUP DELCONSUMER %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size()); +} + +inline void xlen(Connection &connection, const StringView &key) { + connection.send("XLEN %b", key.data(), key.size()); +} + +inline void xpending(Connection &connection, const StringView &key, const StringView &group) { + connection.send("XPENDING %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xpending_detail(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XPENDING %b %b %b %b %lld", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xpending_per_consumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + connection.send("XPENDING %b %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count, + consumer.data(), consumer.size()); +} + +inline void xrange(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end) { + connection.send("XRANGE %b %b %b", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size()); +} + +inline void xrange_count(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xread(Connection &connection, + const StringView &key, + const StringView &id, + long long count) { + connection.send("XREAD COUNT %lld STREAMS %b %b", + count, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_range(Connection &connection, Input first, Input last, long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xread_block(Connection &connection, + const StringView &key, + const StringView &id, + long long timeout, + long long count) { + connection.send("XREAD COUNT %lld BLOCK %lld STREAMS %b %b", + count, + timeout, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_block_range(Connection &connection, + Input first, + Input last, + long long timeout, + long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "BLOCK" << timeout << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup_block(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_block_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xrevrange(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start) { + connection.send("XREVRANGE %b %b %b", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size()); +} + +inline void xrevrange_count(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + connection.send("XREVRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size(), + count); +} + +void xtrim(Connection &connection, const StringView &key, long long count, bool approx); + +namespace detail { + +void set_bitop(CmdArgs &args, BitOp op); + +void set_update_type(CmdArgs &args, UpdateType type); + +void set_aggregation_type(CmdArgs &args, Aggregation type); + +template +void zinterstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zinterstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +void set_geo_unit(CmdArgs &args, GeoUnit unit); + +void set_georadius_store_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void set_georadius_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +} + +} + +} + +} + +namespace sw { + +namespace redis { + +namespace cmd { + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + + detail::set_bitop(args, op); + + args << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + assert(first != last); + + CmdArgs args; + + args << "ZADD" << key; + + detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + while (first != last) { + // Swap the pair to pair. + args << first->second << first->first; + ++first; + } + + connection.send(args); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zinterstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zunionstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_args.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_args.h new file mode 100644 index 000000000..0beb71e5c --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_args.h @@ -0,0 +1,180 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H + +#include +#include +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +class CmdArgs { +public: + template + CmdArgs& append(Arg &&arg); + + template + CmdArgs& append(Arg &&arg, Args &&...args); + + // All overloads of operator<< are for internal use only. + CmdArgs& operator<<(const StringView &arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& operator<<(T &&arg); + + template + CmdArgs& operator<<(const std::pair &range); + + template + auto operator<<(const std::tuple &) -> + typename std::enable_if::type { + return *this; + } + + template + auto operator<<(const std::tuple &arg) -> + typename std::enable_if::type; + + const char** argv() { + return _argv.data(); + } + + const std::size_t* argv_len() { + return _argv_len.data(); + } + + std::size_t size() const { + return _argv.size(); + } + +private: + // Deep copy. + CmdArgs& _append(std::string arg); + + // Shallow copy. + CmdArgs& _append(const StringView &arg); + + // Shallow copy. + CmdArgs& _append(const char *arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& _append(T &&arg) { + return operator<<(std::forward(arg)); + } + + template + CmdArgs& _append(std::true_type, const std::pair &range); + + template + CmdArgs& _append(std::false_type, const std::pair &range); + + std::vector _argv; + std::vector _argv_len; + + std::list _args; +}; + +template +inline CmdArgs& CmdArgs::append(Arg &&arg) { + return _append(std::forward(arg)); +} + +template +inline CmdArgs& CmdArgs::append(Arg &&arg, Args &&...args) { + _append(std::forward(arg)); + + return append(std::forward(args)...); +} + +inline CmdArgs& CmdArgs::operator<<(const StringView &arg) { + _argv.push_back(arg.data()); + _argv_len.push_back(arg.size()); + + return *this; +} + +template +inline CmdArgs& CmdArgs::operator<<(const std::pair &range) { + return _append(IsKvPair())>::type>(), range); +} + +template ::type>::value, + int>::type> +inline CmdArgs& CmdArgs::operator<<(T &&arg) { + return _append(std::to_string(std::forward(arg))); +} + +template +auto CmdArgs::operator<<(const std::tuple &arg) -> + typename std::enable_if::type { + operator<<(std::get(arg)); + + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(std::string arg) { + _args.push_back(std::move(arg)); + return operator<<(_args.back()); +} + +inline CmdArgs& CmdArgs::_append(const StringView &arg) { + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(const char *arg) { + return operator<<(arg); +} + +template +CmdArgs& CmdArgs::_append(std::false_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << *first; + ++first; + } + + return *this; +} + +template +CmdArgs& CmdArgs::_append(std::true_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << first->first << first->second; + ++first; + } + + return *this; +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_options.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_options.h new file mode 100644 index 000000000..ca766c086 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_options.h @@ -0,0 +1,211 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H + +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +enum class UpdateType { + EXIST, + NOT_EXIST, + ALWAYS +}; + +enum class InsertPosition { + BEFORE, + AFTER +}; + +enum class BoundType { + CLOSED, + OPEN, + LEFT_OPEN, + RIGHT_OPEN +}; + +// (-inf, +inf) +template +class UnboundedInterval; + +// [min, max], (min, max), (min, max], [min, max) +template +class BoundedInterval; + +// [min, +inf), (min, +inf) +template +class LeftBoundedInterval; + +// (-inf, max], (-inf, max) +template +class RightBoundedInterval; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(double min, double max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(double min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(double max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(const std::string &min, const std::string &max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(const std::string &min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(const std::string &max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +struct LimitOptions { + long long offset = 0; + long long count = -1; +}; + +enum class Aggregation { + SUM, + MIN, + MAX +}; + +enum class BitOp { + AND, + OR, + XOR, + NOT +}; + +enum class GeoUnit { + M, + KM, + MI, + FT +}; + +template +struct WithCoord : TupleWithType, T> {}; + +template +struct WithDist : TupleWithType {}; + +template +struct WithHash : TupleWithType {}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection.h new file mode 100644 index 000000000..5ad419225 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection.h @@ -0,0 +1,194 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "reply.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +enum class ConnectionType { + TCP = 0, + UNIX +}; + +struct ConnectionOptions { +public: + ConnectionOptions() = default; + + explicit ConnectionOptions(const std::string &uri); + + ConnectionOptions(const ConnectionOptions &) = default; + ConnectionOptions& operator=(const ConnectionOptions &) = default; + + ConnectionOptions(ConnectionOptions &&) = default; + ConnectionOptions& operator=(ConnectionOptions &&) = default; + + ~ConnectionOptions() = default; + + ConnectionType type = ConnectionType::TCP; + + std::string host; + + int port = 6379; + + std::string path; + + std::string password; + + int db = 0; + + bool keep_alive = false; + + std::chrono::milliseconds connect_timeout{0}; + + std::chrono::milliseconds socket_timeout{0}; + +private: + ConnectionOptions _parse_options(const std::string &uri) const; + + ConnectionOptions _parse_tcp_options(const std::string &path) const; + + ConnectionOptions _parse_unix_options(const std::string &path) const; + + auto _split_string(const std::string &str, const std::string &delimiter) const -> + std::pair; +}; + +class CmdArgs; + +class Connection { +public: + explicit Connection(const ConnectionOptions &opts); + + Connection(const Connection &) = delete; + Connection& operator=(const Connection &) = delete; + + Connection(Connection &&) = default; + Connection& operator=(Connection &&) = default; + + ~Connection() = default; + + // Check if the connection is broken. Client needs to do this check + // before sending some command to the connection. If it's broken, + // client needs to reconnect it. + bool broken() const noexcept { + return _ctx->err != REDIS_OK; + } + + void reset() noexcept { + _ctx->err = 0; + } + + void reconnect(); + + auto last_active() const + -> std::chrono::time_point { + return _last_active; + } + + template + void send(const char *format, Args &&...args); + + void send(int argc, const char **argv, const std::size_t *argv_len); + + void send(CmdArgs &args); + + ReplyUPtr recv(); + + const ConnectionOptions& options() const { + return _opts; + } + + friend void swap(Connection &lhs, Connection &rhs) noexcept; + +private: + class Connector; + + struct ContextDeleter { + void operator()(redisContext *context) const { + if (context != nullptr) { + redisFree(context); + } + }; + }; + + using ContextUPtr = std::unique_ptr; + + void _set_options(); + + void _auth(); + + void _select_db(); + + redisContext* _context(); + + ContextUPtr _ctx; + + // The time that the connection is created or the time that + // the connection is used, i.e. *context()* is called. + std::chrono::time_point _last_active{}; + + ConnectionOptions _opts; +}; + +using ConnectionSPtr = std::shared_ptr; + +enum class Role { + MASTER, + SLAVE +}; + +// Inline implementaions. + +template +inline void Connection::send(const char *format, Args &&...args) { + auto ctx = _context(); + + assert(ctx != nullptr); + + if (redisAppendCommand(ctx, + format, + std::forward(args)...) != REDIS_OK) { + throw_error(*ctx, "Failed to send command"); + } + + assert(!broken()); +} + +inline redisContext* Connection::_context() { + _last_active = std::chrono::steady_clock::now(); + + return _ctx.get(); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection_pool.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection_pool.h new file mode 100644 index 000000000..6f2663ad7 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection_pool.h @@ -0,0 +1,115 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +struct ConnectionPoolOptions { + // Max number of connections, including both in-use and idle ones. + std::size_t size = 1; + + // Max time to wait for a connection. 0ms means client waits forever. + std::chrono::milliseconds wait_timeout{0}; + + // Max lifetime of a connection. 0ms means we never expire the connection. + std::chrono::milliseconds connection_lifetime{0}; +}; + +class ConnectionPool { +public: + ConnectionPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool(SimpleSentinel sentinel, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool() = default; + + ConnectionPool(ConnectionPool &&that); + ConnectionPool& operator=(ConnectionPool &&that); + + ConnectionPool(const ConnectionPool &) = delete; + ConnectionPool& operator=(const ConnectionPool &) = delete; + + ~ConnectionPool() = default; + + // Fetch a connection from pool. + Connection fetch(); + + ConnectionOptions connection_options(); + + void release(Connection connection); + + // Create a new connection. + Connection create(); + +private: + void _move(ConnectionPool &&that); + + // NOT thread-safe + Connection _create(); + + Connection _create(SimpleSentinel &sentinel, const ConnectionOptions &opts, bool locked); + + Connection _fetch(); + + void _wait_for_connection(std::unique_lock &lock); + + bool _need_reconnect(const Connection &connection, + const std::chrono::milliseconds &connection_lifetime) const; + + void _update_connection_opts(const std::string &host, int port) { + _opts.host = host; + _opts.port = port; + } + + bool _role_changed(const ConnectionOptions &opts) const { + return opts.port != _opts.port || opts.host != _opts.host; + } + + ConnectionOptions _opts; + + ConnectionPoolOptions _pool_opts; + + std::deque _pool; + + std::size_t _used_connections = 0; + + std::mutex _mutex; + + std::condition_variable _cv; + + SimpleSentinel _sentinel; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/errors.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/errors.h new file mode 100644 index 000000000..44d629e50 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/errors.h @@ -0,0 +1,159 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ERRORS_H +#define SEWENEW_REDISPLUSPLUS_ERRORS_H + +#include +#include +#include + +namespace sw { + +namespace redis { + +enum ReplyErrorType { + ERR, + MOVED, + ASK +}; + +class Error : public std::exception { +public: + explicit Error(const std::string &msg) : _msg(msg) {} + + Error(const Error &) = default; + Error& operator=(const Error &) = default; + + Error(Error &&) = default; + Error& operator=(Error &&) = default; + + virtual ~Error() = default; + + virtual const char* what() const noexcept { + return _msg.data(); + } + +private: + std::string _msg; +}; + +class IoError : public Error { +public: + explicit IoError(const std::string &msg) : Error(msg) {} + + IoError(const IoError &) = default; + IoError& operator=(const IoError &) = default; + + IoError(IoError &&) = default; + IoError& operator=(IoError &&) = default; + + virtual ~IoError() = default; +}; + +class TimeoutError : public IoError { +public: + explicit TimeoutError(const std::string &msg) : IoError(msg) {} + + TimeoutError(const TimeoutError &) = default; + TimeoutError& operator=(const TimeoutError &) = default; + + TimeoutError(TimeoutError &&) = default; + TimeoutError& operator=(TimeoutError &&) = default; + + virtual ~TimeoutError() = default; +}; + +class ClosedError : public Error { +public: + explicit ClosedError(const std::string &msg) : Error(msg) {} + + ClosedError(const ClosedError &) = default; + ClosedError& operator=(const ClosedError &) = default; + + ClosedError(ClosedError &&) = default; + ClosedError& operator=(ClosedError &&) = default; + + virtual ~ClosedError() = default; +}; + +class ProtoError : public Error { +public: + explicit ProtoError(const std::string &msg) : Error(msg) {} + + ProtoError(const ProtoError &) = default; + ProtoError& operator=(const ProtoError &) = default; + + ProtoError(ProtoError &&) = default; + ProtoError& operator=(ProtoError &&) = default; + + virtual ~ProtoError() = default; +}; + +class OomError : public Error { +public: + explicit OomError(const std::string &msg) : Error(msg) {} + + OomError(const OomError &) = default; + OomError& operator=(const OomError &) = default; + + OomError(OomError &&) = default; + OomError& operator=(OomError &&) = default; + + virtual ~OomError() = default; +}; + +class ReplyError : public Error { +public: + explicit ReplyError(const std::string &msg) : Error(msg) {} + + ReplyError(const ReplyError &) = default; + ReplyError& operator=(const ReplyError &) = default; + + ReplyError(ReplyError &&) = default; + ReplyError& operator=(ReplyError &&) = default; + + virtual ~ReplyError() = default; +}; + +class WatchError : public Error { +public: + explicit WatchError() : Error("Watched key has been modified") {} + + WatchError(const WatchError &) = default; + WatchError& operator=(const WatchError &) = default; + + WatchError(WatchError &&) = default; + WatchError& operator=(WatchError &&) = default; + + virtual ~WatchError() = default; +}; + + +// MovedError and AskError are defined in shards.h +class MovedError; + +class AskError; + +void throw_error(redisContext &context, const std::string &err_info); + +void throw_error(const redisReply &reply); + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ERRORS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/pipeline.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/pipeline.h new file mode 100644 index 000000000..52b01253f --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/pipeline.h @@ -0,0 +1,49 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_PIPELINE_H +#define SEWENEW_REDISPLUSPLUS_PIPELINE_H + +#include +#include +#include "connection.h" + +namespace sw { + +namespace redis { + +class PipelineImpl { +public: + template + void command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + } + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t /*cmd_num*/) { + // Reconnect to Redis to discard all commands. + connection.reconnect(); + } +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_PIPELINE_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.h new file mode 100644 index 000000000..71d975ee3 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.h @@ -0,0 +1,1844 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H + +#include +#include +#include +#include +#include "connection.h" +#include "utils.h" +#include "reply.h" +#include "command.h" +#include "redis.h" + +namespace sw { + +namespace redis { + +class QueuedReplies; + +// If any command throws, QueuedRedis resets the connection, and becomes invalid. +// In this case, the only thing we can do is to destory the QueuedRedis object. +template +class QueuedRedis { +public: + QueuedRedis(QueuedRedis &&) = default; + QueuedRedis& operator=(QueuedRedis &&) = default; + + // When it destructs, the underlying *Connection* will be closed, + // and any command that has NOT been executed will be ignored. + ~QueuedRedis() = default; + + Redis redis(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type; + + template + QueuedRedis& command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type; + + QueuedReplies exec(); + + void discard(); + + // CONNECTION commands. + + QueuedRedis& auth(const StringView &password) { + return command(cmd::auth, password); + } + + QueuedRedis& echo(const StringView &msg) { + return command(cmd::echo, msg); + } + + QueuedRedis& ping() { + return command(cmd::ping); + } + + QueuedRedis& ping(const StringView &msg) { + return command(cmd::ping, msg); + } + + // We DO NOT support the QUIT command. See *Redis::quit* doc for details. + // + // QueuedRedis& quit(); + + QueuedRedis& select(long long idx) { + return command(cmd::select, idx); + } + + QueuedRedis& swapdb(long long idx1, long long idx2) { + return command(cmd::swapdb, idx1, idx2); + } + + // SERVER commands. + + QueuedRedis& bgrewriteaof() { + return command(cmd::bgrewriteaof); + } + + QueuedRedis& bgsave() { + return command(cmd::bgsave); + } + + QueuedRedis& dbsize() { + return command(cmd::dbsize); + } + + QueuedRedis& flushall(bool async = false) { + return command(cmd::flushall, async); + } + + QueuedRedis& flushdb(bool async = false) { + return command(cmd::flushdb, async); + } + + QueuedRedis& info() { + return command(cmd::info); + } + + QueuedRedis& info(const StringView §ion) { + return command(cmd::info, section); + } + + QueuedRedis& lastsave() { + return command(cmd::lastsave); + } + + QueuedRedis& save() { + return command(cmd::save); + } + + // KEY commands. + + QueuedRedis& del(const StringView &key) { + return command(cmd::del, key); + } + + template + QueuedRedis& del(Input first, Input last) { + return command(cmd::del_range, first, last); + } + + template + QueuedRedis& del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + QueuedRedis& dump(const StringView &key) { + return command(cmd::dump, key); + } + + QueuedRedis& exists(const StringView &key) { + return command(cmd::exists, key); + } + + template + QueuedRedis& exists(Input first, Input last) { + return command(cmd::exists_range, first, last); + } + + template + QueuedRedis& exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + QueuedRedis& expire(const StringView &key, long long timeout) { + return command(cmd::expire, key, timeout); + } + + QueuedRedis& expire(const StringView &key, + const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); + } + + QueuedRedis& expireat(const StringView &key, long long timestamp) { + return command(cmd::expireat, key, timestamp); + } + + QueuedRedis& expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& keys(const StringView &pattern) { + return command(cmd::keys, pattern); + } + + QueuedRedis& move(const StringView &key, long long db) { + return command(cmd::move, key, db); + } + + QueuedRedis& persist(const StringView &key) { + return command(cmd::persist, key); + } + + QueuedRedis& pexpire(const StringView &key, long long timeout) { + return command(cmd::pexpire, key, timeout); + } + + QueuedRedis& pexpire(const StringView &key, + const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); + } + + QueuedRedis& pexpireat(const StringView &key, long long timestamp) { + return command(cmd::pexpireat, key, timestamp); + } + + QueuedRedis& pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& pttl(const StringView &key) { + return command(cmd::pttl, key); + } + + QueuedRedis& randomkey() { + return command(cmd::randomkey); + } + + QueuedRedis& rename(const StringView &key, const StringView &newkey) { + return command(cmd::rename, key, newkey); + } + + QueuedRedis& renamenx(const StringView &key, const StringView &newkey) { + return command(cmd::renamenx, key, newkey); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false) { + return command(cmd::restore, key, val, ttl, replace); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false) { + return restore(key, val, ttl.count(), replace); + } + + // TODO: sort + + QueuedRedis& scan(long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::scan, cursor, pattern, count); + } + + QueuedRedis& scan(long long cursor) { + return scan(cursor, "*", 10); + } + + QueuedRedis& scan(long long cursor, + const StringView &pattern) { + return scan(cursor, pattern, 10); + } + + QueuedRedis& scan(long long cursor, + long long count) { + return scan(cursor, "*", count); + } + + QueuedRedis& touch(const StringView &key) { + return command(cmd::touch, key); + } + + template + QueuedRedis& touch(Input first, Input last) { + return command(cmd::touch_range, first, last); + } + + template + QueuedRedis& touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + QueuedRedis& ttl(const StringView &key) { + return command(cmd::ttl, key); + } + + QueuedRedis& type(const StringView &key) { + return command(cmd::type, key); + } + + QueuedRedis& unlink(const StringView &key) { + return command(cmd::unlink, key); + } + + template + QueuedRedis& unlink(Input first, Input last) { + return command(cmd::unlink_range, first, last); + } + + template + QueuedRedis& unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + QueuedRedis& wait(long long numslaves, long long timeout) { + return command(cmd::wait, numslaves, timeout); + } + + QueuedRedis& wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); + } + + // STRING commands. + + QueuedRedis& append(const StringView &key, const StringView &str) { + return command(cmd::append, key, str); + } + + QueuedRedis& bitcount(const StringView &key, + long long start = 0, + long long end = -1) { + return command(cmd::bitcount, key, start, end); + } + + QueuedRedis& bitop(BitOp op, + const StringView &destination, + const StringView &key) { + return command(cmd::bitop, op, destination, key); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + Input first, + Input last) { + return command(cmd::bitop_range, op, destination, first, last); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + QueuedRedis& bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1) { + return command(cmd::bitpos, key, bit, start, end); + } + + QueuedRedis& decr(const StringView &key) { + return command(cmd::decr, key); + } + + QueuedRedis& decrby(const StringView &key, long long decrement) { + return command(cmd::decrby, key, decrement); + } + + QueuedRedis& get(const StringView &key) { + return command(cmd::get, key); + } + + QueuedRedis& getbit(const StringView &key, long long offset) { + return command(cmd::getbit, key, offset); + } + + QueuedRedis& getrange(const StringView &key, long long start, long long end) { + return command(cmd::getrange, key, start, end); + } + + QueuedRedis& getset(const StringView &key, const StringView &val) { + return command(cmd::getset, key, val); + } + + QueuedRedis& incr(const StringView &key) { + return command(cmd::incr, key); + } + + QueuedRedis& incrby(const StringView &key, long long increment) { + return command(cmd::incrby, key, increment); + } + + QueuedRedis& incrbyfloat(const StringView &key, double increment) { + return command(cmd::incrbyfloat, key, increment); + } + + template + QueuedRedis& mget(Input first, Input last) { + return command(cmd::mget, first, last); + } + + template + QueuedRedis& mget(std::initializer_list il) { + return mget(il.begin(), il.end()); + } + + template + QueuedRedis& mset(Input first, Input last) { + return command(cmd::mset, first, last); + } + + template + QueuedRedis& mset(std::initializer_list il) { + return mset(il.begin(), il.end()); + } + + template + QueuedRedis& msetnx(Input first, Input last) { + return command(cmd::msetnx, first, last); + } + + template + QueuedRedis& msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + QueuedRedis& psetex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::psetex, key, ttl, val); + } + + QueuedRedis& psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); + } + + QueuedRedis& set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS) { + _set_cmd_indexes.push_back(_cmd_num); + + return command(cmd::set, key, val, ttl.count(), type); + } + + QueuedRedis& setex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::setex, key, ttl, val); + } + + QueuedRedis& setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + return setex(key, ttl.count(), val); + } + + QueuedRedis& setnx(const StringView &key, const StringView &val) { + return command(cmd::setnx, key, val); + } + + QueuedRedis& setrange(const StringView &key, + long long offset, + const StringView &val) { + return command(cmd::setrange, key, offset, val); + } + + QueuedRedis& strlen(const StringView &key) { + return command(cmd::strlen, key); + } + + // LIST commands. + + QueuedRedis& blpop(const StringView &key, long long timeout) { + return command(cmd::blpop, key, timeout); + } + + QueuedRedis& blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(key, timeout.count()); + } + + template + QueuedRedis& blpop(Input first, Input last, long long timeout) { + return command(cmd::blpop_range, first, last, timeout); + } + + template + QueuedRedis& blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(first, last, timeout.count()); + } + + template + QueuedRedis& blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpop(const StringView &key, long long timeout) { + return command(cmd::brpop, key, timeout); + } + + QueuedRedis& brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(key, timeout.count()); + } + + template + QueuedRedis& brpop(Input first, Input last, long long timeout) { + return command(cmd::brpop_range, first, last, timeout); + } + + template + QueuedRedis& brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(first, last, timeout.count()); + } + + template + QueuedRedis& brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + long long timeout) { + return command(cmd::brpoplpush, source, destination, timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpoplpush(source, destination, timeout.count()); + } + + QueuedRedis& lindex(const StringView &key, long long index) { + return command(cmd::lindex, key, index); + } + + QueuedRedis& linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val) { + return command(cmd::linsert, key, position, pivot, val); + } + + QueuedRedis& llen(const StringView &key) { + return command(cmd::llen, key); + } + + QueuedRedis& lpop(const StringView &key) { + return command(cmd::lpop, key); + } + + QueuedRedis& lpush(const StringView &key, const StringView &val) { + return command(cmd::lpush, key, val); + } + + template + QueuedRedis& lpush(const StringView &key, Input first, Input last) { + return command(cmd::lpush_range, key, first, last); + } + + template + QueuedRedis& lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + QueuedRedis& lpushx(const StringView &key, const StringView &val) { + return command(cmd::lpushx, key, val); + } + + QueuedRedis& lrange(const StringView &key, + long long start, + long long stop) { + return command(cmd::lrange, key, start, stop); + } + + QueuedRedis& lrem(const StringView &key, long long count, const StringView &val) { + return command(cmd::lrem, key, count, val); + } + + QueuedRedis& lset(const StringView &key, long long index, const StringView &val) { + return command(cmd::lset, key, index, val); + } + + QueuedRedis& ltrim(const StringView &key, long long start, long long stop) { + return command(cmd::ltrim, key, start, stop); + } + + QueuedRedis& rpop(const StringView &key) { + return command(cmd::rpop, key); + } + + QueuedRedis& rpoplpush(const StringView &source, const StringView &destination) { + return command(cmd::rpoplpush, source, destination); + } + + QueuedRedis& rpush(const StringView &key, const StringView &val) { + return command(cmd::rpush, key, val); + } + + template + QueuedRedis& rpush(const StringView &key, Input first, Input last) { + return command(cmd::rpush_range, key, first, last); + } + + template + QueuedRedis& rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + QueuedRedis& rpushx(const StringView &key, const StringView &val) { + return command(cmd::rpushx, key, val); + } + + // HASH commands. + + QueuedRedis& hdel(const StringView &key, const StringView &field) { + return command(cmd::hdel, key, field); + } + + template + QueuedRedis& hdel(const StringView &key, Input first, Input last) { + return command(cmd::hdel_range, key, first, last); + } + + template + QueuedRedis& hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + QueuedRedis& hexists(const StringView &key, const StringView &field) { + return command(cmd::hexists, key, field); + } + + QueuedRedis& hget(const StringView &key, const StringView &field) { + return command(cmd::hget, key, field); + } + + QueuedRedis& hgetall(const StringView &key) { + return command(cmd::hgetall, key); + } + + QueuedRedis& hincrby(const StringView &key, + const StringView &field, + long long increment) { + return command(cmd::hincrby, key, field, increment); + } + + QueuedRedis& hincrbyfloat(const StringView &key, + const StringView &field, + double increment) { + return command(cmd::hincrbyfloat, key, field, increment); + } + + QueuedRedis& hkeys(const StringView &key) { + return command(cmd::hkeys, key); + } + + QueuedRedis& hlen(const StringView &key) { + return command(cmd::hlen, key); + } + + template + QueuedRedis& hmget(const StringView &key, Input first, Input last) { + return command(cmd::hmget, key, first, last); + } + + template + QueuedRedis& hmget(const StringView &key, std::initializer_list il) { + return hmget(key, il.begin(), il.end()); + } + + template + QueuedRedis& hmset(const StringView &key, Input first, Input last) { + return command(cmd::hmset, key, first, last); + } + + template + QueuedRedis& hmset(const StringView &key, std::initializer_list il) { + return hmset(key, il.begin(), il.end()); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::hscan, key, cursor, pattern, count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return hscan(key, cursor, pattern, 10); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + long long count) { + return hscan(key, cursor, "*", count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor) { + return hscan(key, cursor, "*", 10); + } + + QueuedRedis& hset(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hset, key, field, val); + } + + QueuedRedis& hset(const StringView &key, const std::pair &item) { + return hset(key, item.first, item.second); + } + + QueuedRedis& hsetnx(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hsetnx, key, field, val); + } + + QueuedRedis& hsetnx(const StringView &key, const std::pair &item) { + return hsetnx(key, item.first, item.second); + } + + QueuedRedis& hstrlen(const StringView &key, const StringView &field) { + return command(cmd::hstrlen, key, field); + } + + QueuedRedis& hvals(const StringView &key) { + return command(cmd::hvals, key); + } + + // SET commands. + + QueuedRedis& sadd(const StringView &key, const StringView &member) { + return command(cmd::sadd, key, member); + } + + template + QueuedRedis& sadd(const StringView &key, Input first, Input last) { + return command(cmd::sadd_range, key, first, last); + } + + template + QueuedRedis& sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + QueuedRedis& scard(const StringView &key) { + return command(cmd::scard, key); + } + + template + QueuedRedis& sdiff(Input first, Input last) { + return command(cmd::sdiff, first, last); + } + + template + QueuedRedis& sdiff(std::initializer_list il) { + return sdiff(il.begin(), il.end()); + } + + QueuedRedis& sdiffstore(const StringView &destination, const StringView &key) { + return command(cmd::sdiffstore, destination, key); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, + Input first, + Input last) { + return command(cmd::sdiffstore_range, destination, first, last); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + QueuedRedis& sinter(Input first, Input last) { + return command(cmd::sinter, first, last); + } + + template + QueuedRedis& sinter(std::initializer_list il) { + return sinter(il.begin(), il.end()); + } + + QueuedRedis& sinterstore(const StringView &destination, const StringView &key) { + return command(cmd::sinterstore, destination, key); + } + + template + QueuedRedis& sinterstore(const StringView &destination, + Input first, + Input last) { + return command(cmd::sinterstore_range, destination, first, last); + } + + template + QueuedRedis& sinterstore(const StringView &destination, std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + QueuedRedis& sismember(const StringView &key, const StringView &member) { + return command(cmd::sismember, key, member); + } + + QueuedRedis& smembers(const StringView &key) { + return command(cmd::smembers, key); + } + + QueuedRedis& smove(const StringView &source, + const StringView &destination, + const StringView &member) { + return command(cmd::smove, source, destination, member); + } + + QueuedRedis& spop(const StringView &key) { + return command(cmd::spop, key); + } + + QueuedRedis& spop(const StringView &key, long long count) { + return command(cmd::spop_range, key, count); + } + + QueuedRedis& srandmember(const StringView &key) { + return command(cmd::srandmember, key); + } + + QueuedRedis& srandmember(const StringView &key, long long count) { + return command(cmd::srandmember_range, key, count); + } + + QueuedRedis& srem(const StringView &key, const StringView &member) { + return command(cmd::srem, key, member); + } + + template + QueuedRedis& srem(const StringView &key, Input first, Input last) { + return command(cmd::srem_range, key, first, last); + } + + template + QueuedRedis& srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::sscan, key, cursor, pattern, count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return sscan(key, cursor, pattern, 10); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + long long count) { + return sscan(key, cursor, "*", count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor) { + return sscan(key, cursor, "*", 10); + } + + template + QueuedRedis& sunion(Input first, Input last) { + return command(cmd::sunion, first, last); + } + + template + QueuedRedis& sunion(std::initializer_list il) { + return sunion(il.begin(), il.end()); + } + + QueuedRedis& sunionstore(const StringView &destination, const StringView &key) { + return command(cmd::sunionstore, destination, key); + } + + template + QueuedRedis& sunionstore(const StringView &destination, Input first, Input last) { + return command(cmd::sunionstore_range, destination, first, last); + } + + template + QueuedRedis& sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + QueuedRedis& bzpopmax(const StringView &key, long long timeout) { + return command(cmd::bzpopmax, key, timeout); + } + + QueuedRedis& bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(key, timeout.count()); + } + + template + QueuedRedis& bzpopmax(Input first, Input last, long long timeout) { + return command(cmd::bzpopmax_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, long long timeout) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, long long timeout) { + return command(cmd::bzpopmin, key, timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(key, timeout.count()); + } + + template + QueuedRedis& bzpopmin(Input first, Input last, long long timeout) { + return command(cmd::bzpopmin_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, long long timeout) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + QueuedRedis& zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return command(cmd::zadd, key, member, score, type, changed); + } + + template + QueuedRedis& zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return command(cmd::zadd_range, key, first, last, type, changed); + } + + QueuedRedis& zcard(const StringView &key) { + return command(cmd::zcard, key); + } + + template + QueuedRedis& zcount(const StringView &key, const Interval &interval) { + return command(cmd::zcount, key, interval); + } + + QueuedRedis& zincrby(const StringView &key, double increment, const StringView &member) { + return command(cmd::zincrby, key, increment, member); + } + + QueuedRedis& zinterstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zinterstore, destination, key, weight); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + return command(cmd::zinterstore_range, destination, first, last, type); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + QueuedRedis& zlexcount(const StringView &key, const Interval &interval) { + return command(cmd::zlexcount, key, interval); + } + + QueuedRedis& zpopmax(const StringView &key) { + return command(cmd::zpopmax, key, 1); + } + + QueuedRedis& zpopmax(const StringView &key, long long count) { + return command(cmd::zpopmax, key, count); + } + + QueuedRedis& zpopmin(const StringView &key) { + return command(cmd::zpopmin, key, 1); + } + + QueuedRedis& zpopmin(const StringView &key, long long count) { + return command(cmd::zpopmin, key, count); + } + + // NOTE: *QueuedRedis::zrange*'s parameters are different from *Redis::zrange*. + // *Redis::zrange* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::zrange*. So we have to use an extra parameter: *with_scores*, + // to decide whether we should send *WITHSCORES* option to Redis. This also applies to + // other commands with the *WITHSCORES* option, e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, + // *ZREVRANGEBYSCORE*. + QueuedRedis& zrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrangebylex(const StringView &key, const Interval &interval) { + return zrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrank(const StringView &key, const StringView &member) { + return command(cmd::zrank, key, member); + } + + QueuedRedis& zrem(const StringView &key, const StringView &member) { + return command(cmd::zrem, key, member); + } + + template + QueuedRedis& zrem(const StringView &key, Input first, Input last) { + return command(cmd::zrem_range, key, first, last); + } + + template + QueuedRedis& zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + QueuedRedis& zremrangebylex(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebylex, key, interval); + } + + QueuedRedis& zremrangebyrank(const StringView &key, long long start, long long stop) { + return command(cmd::zremrangebyrank, key, start, stop); + } + + template + QueuedRedis& zremrangebyscore(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebyscore, key, interval); + } + + // See comments on *ZRANGE*. + QueuedRedis& zrevrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrevrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrevrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, const Interval &interval) { + return zrevrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrevrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrevrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrevrank(const StringView &key, const StringView &member) { + return command(cmd::zrevrank, key, member); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::zscan, key, cursor, pattern, count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return zscan(key, cursor, pattern, 10); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + long long count) { + return zscan(key, cursor, "*", count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor) { + return zscan(key, cursor, "*", 10); + } + + QueuedRedis& zscore(const StringView &key, const StringView &member) { + return command(cmd::zscore, key, member); + } + + QueuedRedis& zunionstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zunionstore, destination, key, weight); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + return command(cmd::zunionstore_range, destination, first, last, type); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + QueuedRedis& pfadd(const StringView &key, const StringView &element) { + return command(cmd::pfadd, key, element); + } + + template + QueuedRedis& pfadd(const StringView &key, Input first, Input last) { + return command(cmd::pfadd_range, key, first, last); + } + + template + QueuedRedis& pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + QueuedRedis& pfcount(const StringView &key) { + return command(cmd::pfcount, key); + } + + template + QueuedRedis& pfcount(Input first, Input last) { + return command(cmd::pfcount_range, first, last); + } + + template + QueuedRedis& pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + QueuedRedis& pfmerge(const StringView &destination, const StringView &key) { + return command(cmd::pfmerge, destination, key); + } + + template + QueuedRedis& pfmerge(const StringView &destination, Input first, Input last) { + return command(cmd::pfmerge_range, destination, first, last); + } + + template + QueuedRedis& pfmerge(const StringView &destination, std::initializer_list il) { + return pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + QueuedRedis& geoadd(const StringView &key, + const std::tuple &member) { + return command(cmd::geoadd, key, member); + } + + template + QueuedRedis& geoadd(const StringView &key, + Input first, + Input last) { + return command(cmd::geoadd_range, key, first, last); + } + + template + QueuedRedis& geoadd(const StringView &key, std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + QueuedRedis& geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M) { + return command(cmd::geodist, key, member1, member2, unit); + } + + template + QueuedRedis& geohash(const StringView &key, Input first, Input last) { + return command(cmd::geohash_range, key, first, last); + } + + template + QueuedRedis& geohash(const StringView &key, std::initializer_list il) { + return geohash(key, il.begin(), il.end()); + } + + template + QueuedRedis& geopos(const StringView &key, Input first, Input last) { + return command(cmd::geopos_range, key, first, last); + } + + template + QueuedRedis& geopos(const StringView &key, std::initializer_list il) { + return geopos(key, il.begin(), il.end()); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _georadius_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadius_store, + key, + loc, + radius, + unit, + destination, + store_dist, + count); + } + + // NOTE: *QueuedRedis::georadius*'s parameters are different from *Redis::georadius*. + // *Redis::georadius* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::georadius*. So we have to use extra parameters to decide + // whether we should send options to Redis. This also applies to *GEORADIUSBYMEMBER*. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _georadius_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + destination, + store_dist, + count); + } + + // See the comments on *GEORADIUS*. + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + // SCRIPTING commands. + + QueuedRedis& eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return command(cmd::eval, script, keys, args); + } + + QueuedRedis& evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return command(cmd::evalsha, script, keys, args); + } + + template + QueuedRedis& script_exists(Input first, Input last) { + return command(cmd::script_exists_range, first, last); + } + + template + QueuedRedis& script_exists(std::initializer_list il) { + return script_exists(il.begin(), il.end()); + } + + QueuedRedis& script_flush() { + return command(cmd::script_flush); + } + + QueuedRedis& script_kill() { + return command(cmd::script_kill); + } + + QueuedRedis& script_load(const StringView &script) { + return command(cmd::script_load, script); + } + + // PUBSUB commands. + + QueuedRedis& publish(const StringView &channel, const StringView &message) { + return command(cmd::publish, channel, message); + } + + // Stream commands. + + QueuedRedis& xack(const StringView &key, const StringView &group, const StringView &id) { + return command(cmd::xack, key, group, id); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, Input first, Input last) { + return command(cmd::xack_range, key, group, first, last); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, Input first, Input last) { + return command(cmd::xadd_range, key, id, first, last); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true) { + return command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id) { + return command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last) { + return command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il) { + return xclaim(key, group, consumer, min_idle_time, il.begin(), il.end()); + } + + QueuedRedis& xdel(const StringView &key, const StringView &id) { + return command(cmd::xdel, key, id); + } + + template + QueuedRedis& xdel(const StringView &key, Input first, Input last) { + return command(cmd::xdel_range, key, first, last); + } + + template + QueuedRedis& xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + QueuedRedis& xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false) { + return command(cmd::xgroup_create, key, group, id, mkstream); + } + + QueuedRedis& xgroup_setid(const StringView &key, + const StringView &group, + const StringView &id) { + return command(cmd::xgroup_setid, key, group, id); + } + + QueuedRedis& xgroup_destroy(const StringView &key, const StringView &group) { + return command(cmd::xgroup_destroy, key, group); + } + + QueuedRedis& xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer) { + return command(cmd::xgroup_delconsumer, key, group, consumer); + } + + QueuedRedis& xlen(const StringView &key) { + return command(cmd::xlen, key); + } + + QueuedRedis& xpending(const StringView &key, const StringView &group) { + return command(cmd::xpending, key, group); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xpending_detail, key, group, start, end, count); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + return command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end) { + return command(cmd::xrange, key, start, end); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xrange, key, start, end, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id, long long count) { + return command(cmd::xread, key, id, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id) { + return xread(key, id, 0); + } + + template + auto xread(Input first, Input last, long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return command(cmd::xread_range, first, last, count); + } + + template + auto xread(Input first, Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, 0); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return command(cmd::xread_block, key, id, timeout.count(), count); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xread(key, id, timeout, 0); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return command(cmd::xread_block_range, first, last, timeout.count(), count); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, timeout, 0); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + return command(cmd::xreadgroup, group, consumer, key, id, count, noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count) { + return xreadgroup(group, consumer, key, id, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, 0, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) { + return command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return xreadgroup(group, consumer, key, id, timeout, count, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xreadgroup(group, consumer, key, id, timeout, 0, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, 0, false); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start) { + return command(cmd::xrevrange, key, end, start); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + return command(cmd::xrevrange, key, end, start, count); + } + + QueuedRedis& xtrim(const StringView &key, long long count, bool approx = true) { + return command(cmd::xtrim, key, count, approx); + } + +private: + friend class Redis; + + friend class RedisCluster; + + template + QueuedRedis(const ConnectionSPtr &connection, Args &&...args); + + void _sanity_check() const; + + void _reset(); + + void _invalidate(); + + void _rewrite_replies(std::vector &replies) const; + + template + void _rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const; + + ConnectionSPtr _connection; + + Impl _impl; + + std::size_t _cmd_num = 0; + + std::vector _set_cmd_indexes; + + std::vector _georadius_cmd_indexes; + + bool _valid = true; +}; + +class QueuedReplies { +public: + std::size_t size() const; + + redisReply& get(std::size_t idx); + + template + Result get(std::size_t idx); + + template + void get(std::size_t idx, Output output); + +private: + template + friend class QueuedRedis; + + explicit QueuedReplies(std::vector replies) : _replies(std::move(replies)) {} + + void _index_check(std::size_t idx) const; + + std::vector _replies; +}; + +} + +} + +#include "queued_redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.hpp b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.hpp new file mode 100644 index 000000000..409f48aca --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.hpp @@ -0,0 +1,208 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP + +namespace sw { + +namespace redis { + +template +template +QueuedRedis::QueuedRedis(const ConnectionSPtr &connection, Args &&...args) : + _connection(connection), + _impl(std::forward(args)...) { + assert(_connection); +} + +template +Redis QueuedRedis::redis() { + return Redis(_connection); +} + +template +template +auto QueuedRedis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type { + try { + _sanity_check(); + + _impl.command(*_connection, cmd, std::forward(args)...); + + ++_cmd_num; + } catch (const Error &e) { + _invalidate(); + throw; + } + + return *this; +} + +template +template +QueuedRedis& QueuedRedis::command(const StringView &cmd_name, Args &&...args) { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +template +auto QueuedRedis::command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type { + if (first == last) { + throw Error("command: empty range"); + } + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +QueuedReplies QueuedRedis::exec() { + try { + _sanity_check(); + + auto replies = _impl.exec(*_connection, _cmd_num); + + _rewrite_replies(replies); + + _reset(); + + return QueuedReplies(std::move(replies)); + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +void QueuedRedis::discard() { + try { + _sanity_check(); + + _impl.discard(*_connection, _cmd_num); + + _reset(); + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +void QueuedRedis::_sanity_check() const { + if (!_valid) { + throw Error("Not in valid state"); + } + + if (_connection->broken()) { + throw Error("Connection is broken"); + } +} + +template +inline void QueuedRedis::_reset() { + _cmd_num = 0; + + _set_cmd_indexes.clear(); + + _georadius_cmd_indexes.clear(); +} + +template +void QueuedRedis::_invalidate() { + _valid = false; + + _reset(); +} + +template +void QueuedRedis::_rewrite_replies(std::vector &replies) const { + _rewrite_replies(_set_cmd_indexes, reply::rewrite_set_reply, replies); + + _rewrite_replies(_georadius_cmd_indexes, reply::rewrite_georadius_reply, replies); +} + +template +template +void QueuedRedis::_rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const { + for (auto idx : indexes) { + assert(idx < replies.size()); + + auto &reply = replies[idx]; + + assert(reply); + + rewriter(*reply); + } +} + +inline std::size_t QueuedReplies::size() const { + return _replies.size(); +} + +inline redisReply& QueuedReplies::get(std::size_t idx) { + _index_check(idx); + + auto &reply = _replies[idx]; + + assert(reply); + + return *reply; +} + +template +inline Result QueuedReplies::get(std::size_t idx) { + auto &reply = get(idx); + + return reply::parse(reply); +} + +template +inline void QueuedReplies::get(std::size_t idx, Output output) { + auto &reply = get(idx); + + reply::to_array(reply, output); +} + +inline void QueuedReplies::_index_check(std::size_t idx) const { + if (idx >= size()) { + throw Error("Out of range"); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis++.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis++.h new file mode 100644 index 000000000..0da0ebb16 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis++.h @@ -0,0 +1,25 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H +#define SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H + +#include "redis.h" +#include "redis_cluster.h" +#include "queued_redis.h" +#include "sentinel.h" + +#endif // end SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.h new file mode 100644 index 000000000..b54afb96b --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.h @@ -0,0 +1,1523 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_H +#define SEWENEW_REDISPLUSPLUS_REDIS_H + +#include +#include +#include +#include +#include +#include "connection_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class Redis { +public: + Redis(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : _pool(pool_opts, connection_opts) {} + + // Construct Redis instance with URI: + // "tcp://127.0.0.1", "tcp://127.0.0.1:6379", or "unix://path/to/socket" + explicit Redis(const std::string &uri); + + Redis(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role, + const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(SimpleSentinel(sentinel, master_name, role), pool_opts, connection_opts) {} + + Redis(const Redis &) = delete; + Redis& operator=(const Redis &) = delete; + + Redis(Redis &&) = default; + Redis& operator=(Redis &&) = default; + + Pipeline pipeline(); + + Transaction transaction(bool piped = false); + + Subscriber subscriber(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type; + + template + Result command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // CONNECTION commands. + + void auth(const StringView &password); + + std::string echo(const StringView &msg); + + std::string ping(); + + std::string ping(const StringView &msg); + + // After sending QUIT, only the current connection will be close, while + // other connections in the pool is still open. This is a strange behavior. + // So we DO NOT support the QUIT command. If you want to quit connection to + // server, just destroy the Redis object. + // + // void quit(); + + // We get a connection from the pool, and send the SELECT command to switch + // to a specified DB. However, when we try to send other commands to the + // given DB, we might get a different connection from the pool, and these + // commands, in fact, work on other DB. e.g. + // + // redis.select(1); // get a connection from the pool and switch to the 1th DB + // redis.get("key"); // might get another connection from the pool, + // // and try to get 'key' on the default DB + // + // Obviously, this is NOT what we expect. So we DO NOT support SELECT command. + // In order to select a DB, we can specify the DB index with the ConnectionOptions. + // + // However, since Pipeline and Transaction always send multiple commands on a + // single connection, these two classes have a *select* method. + // + // void select(long long idx); + + void swapdb(long long idx1, long long idx2); + + // SERVER commands. + + void bgrewriteaof(); + + void bgsave(); + + long long dbsize(); + + void flushall(bool async = false); + + void flushdb(bool async = false); + + std::string info(); + + std::string info(const StringView §ion); + + long long lastsave(); + + void save(); + + // KEY commands. + + long long del(const StringView &key); + + template + long long del(Input first, Input last); + + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + OptionalString dump(const StringView &key); + + long long exists(const StringView &key); + + template + long long exists(Input first, Input last); + + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + bool expire(const StringView &key, long long timeout); + + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + bool expireat(const StringView &key, long long timestamp); + + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + template + void keys(const StringView &pattern, Output output); + + bool move(const StringView &key, long long db); + + bool persist(const StringView &key); + + bool pexpire(const StringView &key, long long timeout); + + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + bool pexpireat(const StringView &key, long long timestamp); + + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + long long pttl(const StringView &key); + + OptionalString randomkey(); + + void rename(const StringView &key, const StringView &newkey); + + bool renamenx(const StringView &key, const StringView &newkey); + + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + template + long long scan(long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long scan(long long cursor, + Output output); + + template + long long scan(long long cursor, + const StringView &pattern, + Output output); + + template + long long scan(long long cursor, + long long count, + Output output); + + long long touch(const StringView &key); + + template + long long touch(Input first, Input last); + + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + long long ttl(const StringView &key); + + std::string type(const StringView &key); + + long long unlink(const StringView &key); + + template + long long unlink(Input first, Input last); + + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + long long wait(long long numslaves, long long timeout); + + long long wait(long long numslaves, const std::chrono::milliseconds &timeout); + + // STRING commands. + + long long append(const StringView &key, const StringView &str); + + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + long long decr(const StringView &key); + + long long decrby(const StringView &key, long long decrement); + + OptionalString get(const StringView &key); + + long long getbit(const StringView &key, long long offset); + + std::string getrange(const StringView &key, long long start, long long end); + + OptionalString getset(const StringView &key, const StringView &val); + + long long incr(const StringView &key); + + long long incrby(const StringView &key, long long increment); + + double incrbyfloat(const StringView &key, double increment); + + template + void mget(Input first, Input last, Output output); + + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + template + void mset(Input first, Input last); + + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + template + bool msetnx(Input first, Input last); + + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + void setex(const StringView &key, + long long ttl, + const StringView &val); + + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + bool setnx(const StringView &key, const StringView &val); + + long long setrange(const StringView &key, long long offset, const StringView &val); + + long long strlen(const StringView &key); + + // LIST commands. + + OptionalStringPair blpop(const StringView &key, long long timeout); + + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + OptionalStringPair brpop(const StringView &key, long long timeout); + + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + OptionalString lindex(const StringView &key, long long index); + + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + long long llen(const StringView &key); + + OptionalString lpop(const StringView &key); + + long long lpush(const StringView &key, const StringView &val); + + template + long long lpush(const StringView &key, Input first, Input last); + + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + long long lpushx(const StringView &key, const StringView &val); + + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + long long lrem(const StringView &key, long long count, const StringView &val); + + void lset(const StringView &key, long long index, const StringView &val); + + void ltrim(const StringView &key, long long start, long long stop); + + OptionalString rpop(const StringView &key); + + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + long long rpush(const StringView &key, const StringView &val); + + template + long long rpush(const StringView &key, Input first, Input last); + + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + long long hdel(const StringView &key, const StringView &field); + + template + long long hdel(const StringView &key, Input first, Input last); + + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + bool hexists(const StringView &key, const StringView &field); + + OptionalString hget(const StringView &key, const StringView &field); + + template + void hgetall(const StringView &key, Output output); + + long long hincrby(const StringView &key, const StringView &field, long long increment); + + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + template + void hkeys(const StringView &key, Output output); + + long long hlen(const StringView &key); + + template + void hmget(const StringView &key, Input first, Input last, Output output); + + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + template + void hmset(const StringView &key, Input first, Input last); + + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + bool hset(const StringView &key, const StringView &field, const StringView &val); + + bool hset(const StringView &key, const std::pair &item); + + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + bool hsetnx(const StringView &key, const std::pair &item); + + long long hstrlen(const StringView &key, const StringView &field); + + template + void hvals(const StringView &key, Output output); + + // SET commands. + + long long sadd(const StringView &key, const StringView &member); + + template + long long sadd(const StringView &key, Input first, Input last); + + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + long long scard(const StringView &key); + + template + void sdiff(Input first, Input last, Output output); + + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + long long sdiffstore(const StringView &destination, const StringView &key); + + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + void sinter(Input first, Input last, Output output); + + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + long long sinterstore(const StringView &destination, const StringView &key); + + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + bool sismember(const StringView &key, const StringView &member); + + template + void smembers(const StringView &key, Output output); + + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + OptionalString spop(const StringView &key); + + template + void spop(const StringView &key, long long count, Output output); + + OptionalString srandmember(const StringView &key); + + template + void srandmember(const StringView &key, long long count, Output output); + + long long srem(const StringView &key, const StringView &member); + + template + long long srem(const StringView &key, Input first, Input last); + + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + template + void sunion(Input first, Input last, Output output); + + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + long long sunionstore(const StringView &destination, const StringView &key); + + template + long long sunionstore(const StringView &destination, Input first, Input last); + + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + long long zcard(const StringView &key); + + template + long long zcount(const StringView &key, const Interval &interval); + + double zincrby(const StringView &key, double increment, const StringView &member); + + // There's no aggregation type parameter for single key overload, since these 3 types + // have the same effect. + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + // If *Input* is an iterator of a container of string, + // we use the default weight, i.e. 1, and send + // *ZINTERSTORE destination numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + // If *Input* is an iterator of a container of pair, i.e. key-weight pair, + // we send the command with the given weights: + // *ZINTERSTORE destination numkeys key [key ...] + // [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]* + // + // The following code use the default weight: + // + // vector keys = {"k1", "k2", "k3"}; + // redis.zinterstore(destination, keys.begin(), keys.end()); + // + // On the other hand, the following code use the given weights: + // + // vector> keys_with_weights = {{"k1", 1}, {"k2", 2}, {"k3", 3}}; + // redis.zinterstore(destination, keys_with_weights.begin(), keys_with_weights.end()); + // + // NOTE: `keys_with_weights` can also be of type `unordered_map`. + // However, it will be slower than vector>, since we use + // `distance(first, last)` to calculate the *numkeys* parameter. + // + // This also applies to *ZUNIONSTORE* command. + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + long long zlexcount(const StringView &key, const Interval &interval); + + Optional> zpopmax(const StringView &key); + + template + void zpopmax(const StringView &key, long long count, Output output); + + Optional> zpopmin(const StringView &key); + + template + void zpopmin(const StringView &key, long long count, Output output); + + // If *output* is an iterator of a container of string, + // we send *ZRANGE key start stop* command. + // If it's an iterator of a container of pair, + // we send *ZRANGE key start stop WITHSCORES* command. + // + // The following code sends *ZRANGE* without the *WITHSCORES* option: + // + // vector result; + // redis.zrange("key", 0, -1, back_inserter(result)); + // + // On the other hand, the following code sends command with *WITHSCORES* option: + // + // unordered_map with_score; + // redis.zrange("key", 0, -1, inserter(with_score, with_score.end())); + // + // This also applies to other commands with the *WITHSCORES* option, + // e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, *ZREVRANGEBYSCORE*. + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrank(const StringView &key, const StringView &member); + + long long zrem(const StringView &key, const StringView &member); + + template + long long zrem(const StringView &key, Input first, Input last); + + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + OptionalDouble zscore(const StringView &key, const StringView &member); + + // There's no aggregation type parameter for single key overload, since these 3 types + // have the same effect. + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + // See *zinterstore* comment for how to use this method. + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + bool pfadd(const StringView &key, const StringView &element); + + template + bool pfadd(const StringView &key, Input first, Input last); + + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + void script_exists(Input first, Input last, Output output); + + template + void script_exists(std::initializer_list il, Output output) { + script_exists(il.begin(), il.end(), output); + } + + void script_flush(); + + void script_kill(); + + std::string script_load(const StringView &script); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Transaction commands. + void watch(const StringView &key); + + template + void watch(Input first, Input last); + + template + void watch(std::initializer_list il) { + watch(il.begin(), il.end()); + } + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first ,last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + class ConnectionPoolGuard { + public: + ConnectionPoolGuard(ConnectionPool &pool, + Connection &connection) : _pool(pool), _connection(connection) {} + + ~ConnectionPoolGuard() { + _pool.release(std::move(_connection)); + } + + private: + ConnectionPool &_pool; + Connection &_connection; + }; + + template + friend class QueuedRedis; + + friend class RedisCluster; + + // For internal use. + explicit Redis(const ConnectionSPtr &connection); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _command(Connection &connection, Cmd cmd, Args &&...args); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + // Pool Mode. + // Public constructors create a *Redis* instance with a pool. + // In this case, *_connection* is a null pointer, and is never used. + ConnectionPool _pool; + + // Single Connection Mode. + // Private constructor creats a *Redis* instance with a single connection. + // This is used when we create Transaction, Pipeline and Subscriber. + // In this case, *_pool* is empty, and is never used. + ConnectionSPtr _connection; +}; + +} + +} + +#include "redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.hpp b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.hpp new file mode 100644 index 000000000..3a227a6f1 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.hpp @@ -0,0 +1,1365 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_HPP + +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +template +auto Redis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (_connection) { + // Single Connection Mode. + // TODO: In this case, should we reconnect? + if (_connection->broken()) { + throw Error("Connection is broken"); + } + + return _command(*_connection, cmd, std::forward(args)...); + } else { + // Pool Mode, i.e. get connection from pool. + auto connection = _pool.fetch(); + + assert(!connection.broken()); + + ConnectionPoolGuard guard(_pool, connection); + + return _command(connection, cmd, std::forward(args)...); + } +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, ReplyUPtr>::type { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (first == last) { + throw Error("command: empty range"); + } + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +Result Redis::command(const StringView &cmd_name, Args &&...args) { + auto r = command(cmd_name, std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long Redis::del(Input first, Input last) { + if (first == last) { + throw Error("DEL: no key specified"); + } + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::exists(Input first, Input last) { + if (first == last) { + throw Error("EXISTS: no key specified"); + } + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool Redis::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool Redis::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +template +void Redis::keys(const StringView &pattern, Output output) { + auto reply = command(cmd::keys, pattern); + + reply::to_array(*reply, output); +} + +inline bool Redis::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool Redis::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void Redis::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long Redis::scan(long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::scan, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::scan(long long cursor, + const StringView &pattern, + Output output) { + return scan(cursor, pattern, 10, output); +} + +template +inline long long Redis::scan(long long cursor, + long long count, + Output output) { + return scan(cursor, "*", count, output); +} + +template +inline long long Redis::scan(long long cursor, + Output output) { + return scan(cursor, "*", 10, output); +} + +template +long long Redis::touch(Input first, Input last) { + if (first == last) { + throw Error("TOUCH: no key specified"); + } + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::unlink(Input first, Input last) { + if (first == last) { + throw Error("UNLINK: no key specified"); + } + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +inline long long Redis::wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); +} + +// STRING commands. + +template +long long Redis::bitop(BitOp op, const StringView &destination, Input first, Input last) { + if (first == last) { + throw Error("BITOP: no key specified"); + } + + auto reply = command(cmd::bitop_range, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::mget(Input first, Input last, Output output) { + if (first == last) { + throw Error("MGET: no key specified"); + } + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::mset(Input first, Input last) { + if (first == last) { + throw Error("MSET: no key specified"); + } + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool Redis::msetnx(Input first, Input last) { + if (first == last) { + throw Error("MSETNX: no key specified"); + } + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void Redis::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void Redis::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair Redis::blpop(Input first, Input last, long long timeout) { + if (first == last) { + throw Error("BLPOP: no key specified"); + } + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair Redis::brpop(Input first, Input last, long long timeout) { + if (first == last) { + throw Error("BRPOP: no key specified"); + } + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString Redis::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long Redis::lpush(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("LPUSH: no key specified"); + } + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long Redis::rpush(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("RPUSH: no key specified"); + } + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long Redis::hdel(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("HDEL: no key specified"); + } + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmget(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("HMGET: no key specified"); + } + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmset(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("HMSET: no key specified"); + } + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +inline void Redis::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long Redis::sadd(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("SADD: no key specified"); + } + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sdiff(Input first, Input last, Output output) { + if (first == last) { + throw Error("SDIFF: no key specified"); + } + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sdiffstore(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("SDIFFSTORE: no key specified"); + } + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sinter(Input first, Input last, Output output) { + if (first == last) { + throw Error("SINTER: no key specified"); + } + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sinterstore(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("SINTERSTORE: no key specified"); + } + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void Redis::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long Redis::srem(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("SREM: no key specified"); + } + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void Redis::sunion(Input first, Input last, Output output) { + if (first == last) { + throw Error("SUNION: no key specified"); + } + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sunionstore(const StringView &destination, Input first, Input last) { + if (first == last) { + throw Error("SUNIONSTORE: no key specified"); + } + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto Redis::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto Redis::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto Redis::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto Redis::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long Redis::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + if (first == last) { + throw Error("ZADD: no key specified"); + } + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long Redis::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + if (first == last) { + throw Error("ZINTERSTORE: no key specified"); + } + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long Redis::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void Redis::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zrem(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("ZREM: no key specified"); + } + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long Redis::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + if (first == last) { + throw Error("ZUNIONSTORE: no key specified"); + } + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool Redis::pfadd(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("PFADD: no key specified"); + } + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::pfcount(Input first, Input last) { + if (first == last) { + throw Error("PFCOUNT: no key specified"); + } + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void Redis::pfmerge(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("PFMERGE: no key specified"); + } + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long Redis::geoadd(const StringView &key, + Input first, + Input last) { + if (first == last) { + throw Error("GEOADD: no key specified"); + } + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::geohash(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("GEOHASH: no key specified"); + } + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::geopos(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("GEOPOS: no key specified"); + } + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void Redis::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + auto reply = command(cmd::eval, script, keys, args); + + return reply::parse(*reply); +} + +template +void Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + auto reply = command(cmd::eval, script, keys, args); + + reply::to_array(*reply, output); +} + +template +Result Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + auto reply = command(cmd::evalsha, script, keys, args); + + return reply::parse(*reply); +} + +template +void Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + auto reply = command(cmd::evalsha, script, keys, args); + + reply::to_array(*reply, output); +} + +template +void Redis::script_exists(Input first, Input last, Output output) { + if (first == last) { + throw Error("SCRIPT EXISTS: no key specified"); + } + + auto reply = command(cmd::script_exists_range, first, last); + + reply::to_array(*reply, output); +} + +// Transaction commands. + +template +void Redis::watch(Input first, Input last) { + auto reply = command(cmd::watch_range, first, last); + + reply::parse(*reply); +} + +// Stream commands. + +template +long long Redis::xack(const StringView &key, const StringView &group, Input first, Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, const StringView &id, Input first, Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long Redis::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto Redis::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREAD: no key specified"); + } + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREAD: no key specified"); + } + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREADGROUP: no key specified"); + } + + auto reply = command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREADGROUP: no key specified"); + } + + auto reply = command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +ReplyUPtr Redis::_command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + auto reply = connection.recv(); + + return reply; +} + +template +inline ReplyUPtr Redis::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr Redis::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr Redis::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_HPP diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.h new file mode 100644 index 000000000..50a221367 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.h @@ -0,0 +1,1395 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H + +#include +#include +#include +#include +#include "shards_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "redis.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class RedisCluster { +public: + RedisCluster(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(pool_opts, connection_opts) {} + + // Construct RedisCluster with URI: + // "tcp://127.0.0.1" or "tcp://127.0.0.1:6379" + // Only need to specify one URI. + explicit RedisCluster(const std::string &uri); + + RedisCluster(const RedisCluster &) = delete; + RedisCluster& operator=(const RedisCluster &) = delete; + + RedisCluster(RedisCluster &&) = default; + RedisCluster& operator=(RedisCluster &&) = default; + + Redis redis(const StringView &hash_tag); + + Pipeline pipeline(const StringView &hash_tag); + + Transaction transaction(const StringView &hash_tag, bool piped = false); + + Subscriber subscriber(); + + template + auto command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // KEY commands. + + long long del(const StringView &key); + + template + long long del(Input first, Input last); + + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + OptionalString dump(const StringView &key); + + long long exists(const StringView &key); + + template + long long exists(Input first, Input last); + + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + bool expire(const StringView &key, long long timeout); + + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + bool expireat(const StringView &key, long long timestamp); + + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + bool persist(const StringView &key); + + bool pexpire(const StringView &key, long long timeout); + + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + bool pexpireat(const StringView &key, long long timestamp); + + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + long long pttl(const StringView &key); + + void rename(const StringView &key, const StringView &newkey); + + bool renamenx(const StringView &key, const StringView &newkey); + + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + long long touch(const StringView &key); + + template + long long touch(Input first, Input last); + + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + long long ttl(const StringView &key); + + std::string type(const StringView &key); + + long long unlink(const StringView &key); + + template + long long unlink(Input first, Input last); + + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + // STRING commands. + + long long append(const StringView &key, const StringView &str); + + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + long long decr(const StringView &key); + + long long decrby(const StringView &key, long long decrement); + + OptionalString get(const StringView &key); + + long long getbit(const StringView &key, long long offset); + + std::string getrange(const StringView &key, long long start, long long end); + + OptionalString getset(const StringView &key, const StringView &val); + + long long incr(const StringView &key); + + long long incrby(const StringView &key, long long increment); + + double incrbyfloat(const StringView &key, double increment); + + template + void mget(Input first, Input last, Output output); + + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + template + void mset(Input first, Input last); + + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + template + bool msetnx(Input first, Input last); + + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + void setex(const StringView &key, + long long ttl, + const StringView &val); + + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + bool setnx(const StringView &key, const StringView &val); + + long long setrange(const StringView &key, long long offset, const StringView &val); + + long long strlen(const StringView &key); + + // LIST commands. + + OptionalStringPair blpop(const StringView &key, long long timeout); + + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + OptionalStringPair brpop(const StringView &key, long long timeout); + + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + OptionalString lindex(const StringView &key, long long index); + + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + long long llen(const StringView &key); + + OptionalString lpop(const StringView &key); + + long long lpush(const StringView &key, const StringView &val); + + template + long long lpush(const StringView &key, Input first, Input last); + + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + long long lpushx(const StringView &key, const StringView &val); + + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + long long lrem(const StringView &key, long long count, const StringView &val); + + void lset(const StringView &key, long long index, const StringView &val); + + void ltrim(const StringView &key, long long start, long long stop); + + OptionalString rpop(const StringView &key); + + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + long long rpush(const StringView &key, const StringView &val); + + template + long long rpush(const StringView &key, Input first, Input last); + + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + long long hdel(const StringView &key, const StringView &field); + + template + long long hdel(const StringView &key, Input first, Input last); + + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + bool hexists(const StringView &key, const StringView &field); + + OptionalString hget(const StringView &key, const StringView &field); + + template + void hgetall(const StringView &key, Output output); + + long long hincrby(const StringView &key, const StringView &field, long long increment); + + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + template + void hkeys(const StringView &key, Output output); + + long long hlen(const StringView &key); + + template + void hmget(const StringView &key, Input first, Input last, Output output); + + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + template + void hmset(const StringView &key, Input first, Input last); + + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + bool hset(const StringView &key, const StringView &field, const StringView &val); + + bool hset(const StringView &key, const std::pair &item); + + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + bool hsetnx(const StringView &key, const std::pair &item); + + long long hstrlen(const StringView &key, const StringView &field); + + template + void hvals(const StringView &key, Output output); + + // SET commands. + + long long sadd(const StringView &key, const StringView &member); + + template + long long sadd(const StringView &key, Input first, Input last); + + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + long long scard(const StringView &key); + + template + void sdiff(Input first, Input last, Output output); + + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + long long sdiffstore(const StringView &destination, const StringView &key); + + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + void sinter(Input first, Input last, Output output); + + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + long long sinterstore(const StringView &destination, const StringView &key); + + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + bool sismember(const StringView &key, const StringView &member); + + template + void smembers(const StringView &key, Output output); + + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + OptionalString spop(const StringView &key); + + template + void spop(const StringView &key, long long count, Output output); + + OptionalString srandmember(const StringView &key); + + template + void srandmember(const StringView &key, long long count, Output output); + + long long srem(const StringView &key, const StringView &member); + + template + long long srem(const StringView &key, Input first, Input last); + + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + template + void sunion(Input first, Input last, Output output); + + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + long long sunionstore(const StringView &destination, const StringView &key); + + template + long long sunionstore(const StringView &destination, Input first, Input last); + + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + long long zcard(const StringView &key); + + template + long long zcount(const StringView &key, const Interval &interval); + + double zincrby(const StringView &key, double increment, const StringView &member); + + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + long long zlexcount(const StringView &key, const Interval &interval); + + Optional> zpopmax(const StringView &key); + + template + void zpopmax(const StringView &key, long long count, Output output); + + Optional> zpopmin(const StringView &key); + + template + void zpopmin(const StringView &key, long long count, Output output); + + // If *output* is an iterator of a container of string, + // we send *ZRANGE key start stop* command. + // If it's an iterator of a container of pair, + // we send *ZRANGE key start stop WITHSCORES* command. + // + // The following code sends *ZRANGE* without the *WITHSCORES* option: + // + // vector result; + // redis.zrange("key", 0, -1, back_inserter(result)); + // + // On the other hand, the following code sends command with *WITHSCORES* option: + // + // unordered_map with_score; + // redis.zrange("key", 0, -1, inserter(with_score, with_score.end())); + // + // This also applies to other commands with the *WITHSCORES* option, + // e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, *ZREVRANGEBYSCORE*. + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrank(const StringView &key, const StringView &member); + + long long zrem(const StringView &key, const StringView &member); + + template + long long zrem(const StringView &key, Input first, Input last); + + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + OptionalDouble zscore(const StringView &key, const StringView &member); + + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + bool pfadd(const StringView &key, const StringView &element); + + template + bool pfadd(const StringView &key, Input first, Input last); + + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first, last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + class Command { + public: + explicit Command(const StringView &cmd_name) : _cmd_name(cmd_name) {} + + template + void operator()(Connection &connection, Args &&...args) const { + CmdArgs cmd_args; + cmd_args.append(_cmd_name, std::forward(args)...); + connection.send(cmd_args); + } + + private: + StringView _cmd_name; + }; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + ReplyUPtr _command(Cmd cmd, Connection &connection, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::true_type, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::false_type, Input &&first, Args &&...args); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _range_command(Cmd cmd, std::true_type, Input input, Args &&...args); + + template + ReplyUPtr _range_command(Cmd cmd, std::false_type, Input input, Args &&...args); + + void _asking(Connection &connection); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + ShardsPool _pool; +}; + +} + +} + +#include "redis_cluster.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.hpp b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.hpp new file mode 100644 index 000000000..61da3f062 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.hpp @@ -0,0 +1,1415 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP + +#include +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" +#include "shards_pool.h" + +namespace sw { + +namespace redis { + +template +auto RedisCluster::command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + return _command(cmd, + std::is_convertible::type, StringView>(), + std::forward(key), + std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type { + auto cmd = Command(cmd_name); + + return _generic_command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type { + auto r = command(cmd_name, std::forward(key), std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(key), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (first == last || std::next(first) == last) { + throw Error("command: invalid range"); + } + + const auto &key = *first; + ++first; + + auto cmd = [&key](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + cmd_args.append(key); + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long RedisCluster::del(Input first, Input last) { + if (first == last) { + throw Error("DEL: no key specified"); + } + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::exists(Input first, Input last) { + if (first == last) { + throw Error("EXISTS: no key specified"); + } + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool RedisCluster::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool RedisCluster::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +inline bool RedisCluster::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool RedisCluster::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void RedisCluster::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long RedisCluster::touch(Input first, Input last) { + if (first == last) { + throw Error("TOUCH: no key specified"); + } + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::unlink(Input first, Input last) { + if (first == last) { + throw Error("UNLINK: no key specified"); + } + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +// STRING commands. + +template +long long RedisCluster::bitop(BitOp op, const StringView &destination, Input first, Input last) { + if (first == last) { + throw Error("BITOP: no key specified"); + } + + auto reply = _command(cmd::bitop_range, destination, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::mget(Input first, Input last, Output output) { + if (first == last) { + throw Error("MGET: no key specified"); + } + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::mset(Input first, Input last) { + if (first == last) { + throw Error("MSET: no key specified"); + } + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool RedisCluster::msetnx(Input first, Input last) { + if (first == last) { + throw Error("MSETNX: no key specified"); + } + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void RedisCluster::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void RedisCluster::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair RedisCluster::blpop(Input first, Input last, long long timeout) { + if (first == last) { + throw Error("BLPOP: no key specified"); + } + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, Input last, long long timeout) { + if (first == last) { + throw Error("BRPOP: no key specified"); + } + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString RedisCluster::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long RedisCluster::lpush(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("LPUSH: no key specified"); + } + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long RedisCluster::rpush(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("RPUSH: no key specified"); + } + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long RedisCluster::hdel(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("HDEL: no key specified"); + } + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmget(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("HMGET: no key specified"); + } + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmset(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("HMSET: no key specified"); + } + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +inline void RedisCluster::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long RedisCluster::sadd(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("SADD: no key specified"); + } + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sdiff(Input first, Input last, Output output) { + if (first == last) { + throw Error("SDIFF: no key specified"); + } + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sdiffstore(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("SDIFFSTORE: no key specified"); + } + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sinter(Input first, Input last, Output output) { + if (first == last) { + throw Error("SINTER: no key specified"); + } + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sinterstore(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("SINTERSTORE: no key specified"); + } + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::srem(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("SREM: no key specified"); + } + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void RedisCluster::sunion(Input first, Input last, Output output) { + if (first == last) { + throw Error("SUNION: no key specified"); + } + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sunionstore(const StringView &destination, Input first, Input last) { + if (first == last) { + throw Error("SUNIONSTORE: no key specified"); + } + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto RedisCluster::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto RedisCluster::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long RedisCluster::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + if (first == last) { + throw Error("ZADD: no key specified"); + } + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + if (first == last) { + throw Error("ZINTERSTORE: no key specified"); + } + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zrem(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("ZREM: no key specified"); + } + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long RedisCluster::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + if (first == last) { + throw Error("ZUNIONSTORE: no key specified"); + } + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool RedisCluster::pfadd(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("PFADD: no key specified"); + } + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::pfcount(Input first, Input last) { + if (first == last) { + throw Error("PFCOUNT: no key specified"); + } + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::pfmerge(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("PFMERGE: no key specified"); + } + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long RedisCluster::geoadd(const StringView &key, + Input first, + Input last) { + if (first == last) { + throw Error("GEOADD: no key specified"); + } + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::geohash(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("GEOHASH: no key specified"); + } + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::geopos(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("GEOPOS: no key specified"); + } + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + if (keys.size() == 0) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, *keys.begin(), script, keys, args); + + return reply::parse(*reply); +} + +template +void RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + if (keys.size() == 0) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, *keys.begin(), script, keys, args); + + reply::to_array(*reply, output); +} + +template +Result RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + if (keys.size() == 0) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::evalsha, *keys.begin(), script, keys, args); + + return reply::parse(*reply); +} + +template +void RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + if (keys.size() == 0) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = command(cmd::evalsha, *keys.begin(), script, keys, args); + + reply::to_array(*reply, output); +} + +// Stream commands. + +template +long long RedisCluster::xack(const StringView &key, + const StringView &group, + Input first, + Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto RedisCluster::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREAD: no key specified"); + } + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREAD: no key specified"); + } + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup, key, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREADGROUP: no key specified"); + } + + auto reply = _command(cmd::xreadgroup_range, + first->first, + group, + consumer, + first, + last, + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup_block, + key, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREADGROUP: no key specified"); + } + + auto reply = _command(cmd::xreadgroup_block_range, + first->first, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type { + return command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type { + auto k = std::to_string(std::forward(key)); + return command(cmd, k, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::true_type, const StringView &key, Args &&...args) { + return _command(cmd, key, key, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::false_type, Input &&first, Args &&...args) { + return _range_command(cmd, + std::is_convertible< + typename std::decay< + decltype(*std::declval())>::type, StringView>(), + std::forward(first), + std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::true_type, Input input, Args &&...args) { + return _command(cmd, *input, input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::false_type, Input input, Args &&...args) { + return _command(cmd, std::get<0>(*input), input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, Connection &connection, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + return connection.recv(); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, const StringView &key, Args &&...args) { + for (auto idx = 0; idx < 2; ++idx) { + try { + auto guarded_connection = _pool.fetch(key); + + return _command(cmd, guarded_connection.connection(), std::forward(args)...); + } catch (const IoError &err) { + // When master is down, one of its replicas will be promoted to be the new master. + // If we try to send command to the old master, we'll get an *IoError*. + // In this case, we need to update the slots mapping. + _pool.update(); + } catch (const ClosedError &err) { + // Node might be removed. + // 1. Get up-to-date slot mapping to check if the node still exists. + _pool.update(); + + // TODO: + // 2. If it's NOT exist, update slot mapping, and retry. + // 3. If it's still exist, that means the node is down, NOT removed, throw exception. + } catch (const MovedError &err) { + // Slot mapping has been changed, update it and try again. + _pool.update(); + } catch (const AskError &err) { + auto guarded_connection = _pool.fetch(err.node()); + auto &connection = guarded_connection.connection(); + + // 1. send ASKING command. + _asking(connection); + + // 2. resend last command. + try { + return _command(cmd, connection, std::forward(args)...); + } catch (const MovedError &err) { + throw Error("Slot migrating... ASKING node hasn't been set to IMPORTING state"); + } + } // For other exceptions, just throw it. + } + + // Possible failures: + // 1. Source node has already run 'CLUSTER SETSLOT xxx NODE xxx', + // while the destination node has NOT run it. + // In this case, client will be redirected by both nodes with MovedError. + // 2. Other failures... + throw Error("Failed to send command with key: " + std::string(key.data(), key.size())); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr RedisCluster::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/reply.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/reply.h new file mode 100644 index 000000000..b309de5bb --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/reply.h @@ -0,0 +1,363 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REPLY_H +#define SEWENEW_REDISPLUSPLUS_REPLY_H + +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +struct ReplyDeleter { + void operator()(redisReply *reply) const { + if (reply != nullptr) { + freeReplyObject(reply); + } + } +}; + +using ReplyUPtr = std::unique_ptr; + +namespace reply { + +template +struct ParseTag {}; + +template +inline T parse(redisReply &reply) { + return parse(ParseTag(), reply); +} + +void parse(ParseTag, redisReply &reply); + +std::string parse(ParseTag, redisReply &reply); + +long long parse(ParseTag, redisReply &reply); + +double parse(ParseTag, redisReply &reply); + +bool parse(ParseTag, redisReply &reply); + +template +Optional parse(ParseTag>, redisReply &reply); + +template +std::pair parse(ParseTag>, redisReply &reply); + +template +std::tuple parse(ParseTag>, redisReply &reply); + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template +long long parse_scan_reply(redisReply &reply, Output output); + +inline bool is_error(redisReply &reply) { + return reply.type == REDIS_REPLY_ERROR; +} + +inline bool is_nil(redisReply &reply) { + return reply.type == REDIS_REPLY_NIL; +} + +inline bool is_string(redisReply &reply) { + return reply.type == REDIS_REPLY_STRING; +} + +inline bool is_status(redisReply &reply) { + return reply.type == REDIS_REPLY_STATUS; +} + +inline bool is_integer(redisReply &reply) { + return reply.type == REDIS_REPLY_INTEGER; +} + +inline bool is_array(redisReply &reply) { + return reply.type == REDIS_REPLY_ARRAY; +} + +std::string to_status(redisReply &reply); + +template +void to_array(redisReply &reply, Output output); + +// Rewrite set reply to bool type +void rewrite_set_reply(redisReply &reply); + +// Rewrite georadius reply to OptionalLongLong type +void rewrite_georadius_reply(redisReply &reply); + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple; + +} + +// Inline implementations. + +namespace reply { + +namespace detail { + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.element == nullptr) { + // Empty array. + return; + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + auto *sub_reply = reply.element[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null array element reply"); + } + + *output = parse::type>(*sub_reply); + + ++output; + } +} + +bool is_flat_array(redisReply &reply); + +template +void to_flat_array(redisReply &reply, Output output) { + if (reply.element == nullptr) { + // Empty array. + return; + } + + if (reply.elements % 2 != 0) { + throw ProtoError("Not string pair array reply"); + } + + for (std::size_t idx = 0; idx != reply.elements; idx += 2) { + auto *key_reply = reply.element[idx]; + auto *val_reply = reply.element[idx + 1]; + if (key_reply == nullptr || val_reply == nullptr) { + throw ProtoError("Null string array reply"); + } + + using Pair = typename IterType::type; + using FirstType = typename std::decay::type; + using SecondType = typename std::decay::type; + *output = std::make_pair(parse(*key_reply), + parse(*val_reply)); + + ++output; + } +} + +template +void to_array(std::true_type, redisReply &reply, Output output) { + if (is_flat_array(reply)) { + to_flat_array(reply, output); + } else { + to_array(reply, output); + } +} + +template +void to_array(std::false_type, redisReply &reply, Output output) { + to_array(reply, output); +} + +template +std::tuple parse_tuple(redisReply **reply, std::size_t idx) { + assert(reply != nullptr); + + auto *sub_reply = reply[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null reply"); + } + + return std::make_tuple(parse(*sub_reply)); +} + +template +auto parse_tuple(redisReply **reply, std::size_t idx) -> + typename std::enable_if>::type { + assert(reply != nullptr); + + return std::tuple_cat(parse_tuple(reply, idx), + parse_tuple(reply, idx + 1)); +} + +} + +template +Optional parse(ParseTag>, redisReply &reply) { + if (reply::is_nil(reply)) { + return {}; + } + + return Optional(parse(reply)); +} + +template +std::pair parse(ParseTag>, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != 2) { + throw ProtoError("NOT key-value PAIR reply"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null PAIR reply"); + } + + auto *first = reply.element[0]; + auto *second = reply.element[1]; + if (first == nullptr || second == nullptr) { + throw ProtoError("Null pair reply"); + } + + return std::make_pair(parse::type>(*first), + parse::type>(*second)); +} + +template +std::tuple parse(ParseTag>, redisReply &reply) { + constexpr auto size = sizeof...(Args); + + static_assert(size > 0, "DO NOT support parsing tuple with 0 element"); + + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != size) { + throw ProtoError("Expect tuple reply with " + std::to_string(size) + "elements"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null TUPLE reply"); + } + + return detail::parse_tuple(reply.element, 0); +} + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::back_inserter(container)); + + return container; +} + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::inserter(container, container.end())); + + return container; +} + +template +long long parse_scan_reply(redisReply &reply, Output output) { + if (reply.elements != 2 || reply.element == nullptr) { + throw ProtoError("Invalid scan reply"); + } + + auto *cursor_reply = reply.element[0]; + auto *data_reply = reply.element[1]; + if (cursor_reply == nullptr || data_reply == nullptr) { + throw ProtoError("Invalid cursor reply or data reply"); + } + + auto cursor_str = reply::parse(*cursor_reply); + auto new_cursor = 0; + try { + new_cursor = std::stoll(cursor_str); + } catch (const std::exception &e) { + throw ProtoError("Invalid cursor reply: " + cursor_str); + } + + reply::to_array(*data_reply, output); + + return new_cursor; +} + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + detail::to_array(typename IsKvPairIter::type(), reply, output); +} + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple { + if (!is_array(reply) || reply.elements != 4) { + throw ProtoError("expect array reply with 4 elements"); + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + if (reply.element[idx] == nullptr) { + throw ProtoError("null array reply"); + } + } + + auto num = parse(*(reply.element[0])); + auto start = parse(*(reply.element[1])); + auto end = parse(*(reply.element[2])); + + auto &entry_reply = *(reply.element[3]); + if (!is_nil(entry_reply)) { + to_array(entry_reply, output); + } + + return std::make_tuple(num, std::move(start), std::move(end)); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REPLY_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/sentinel.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/sentinel.h new file mode 100644 index 000000000..e80d1e56a --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/sentinel.h @@ -0,0 +1,138 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SENTINEL_H +#define SEWENEW_REDISPLUSPLUS_SENTINEL_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "shards.h" +#include "reply.h" + +namespace sw { + +namespace redis { + +struct SentinelOptions { + std::vector> nodes; + + std::string password; + + bool keep_alive = true; + + std::chrono::milliseconds connect_timeout{100}; + + std::chrono::milliseconds socket_timeout{100}; + + std::chrono::milliseconds retry_interval{100}; + + std::size_t max_retry = 2; +}; + +class Sentinel { +public: + explicit Sentinel(const SentinelOptions &sentinel_opts); + + Sentinel(const Sentinel &) = delete; + Sentinel& operator=(const Sentinel &) = delete; + + Sentinel(Sentinel &&) = delete; + Sentinel& operator=(Sentinel &&) = delete; + + ~Sentinel() = default; + +private: + Connection master(const std::string &master_name, const ConnectionOptions &opts); + + Connection slave(const std::string &master_name, const ConnectionOptions &opts); + + class Iterator; + + friend class SimpleSentinel; + + std::list _parse_options(const SentinelOptions &opts) const; + + Optional _get_master_addr_by_name(Connection &connection, const StringView &name); + + std::vector _get_slave_addr_by_name(Connection &connection, const StringView &name); + + Connection _connect_redis(const Node &node, ConnectionOptions opts); + + Role _get_role(Connection &connection); + + std::vector _parse_slave_info(redisReply &reply) const; + + std::list _healthy_sentinels; + + std::list _broken_sentinels; + + SentinelOptions _sentinel_opts; + + std::mutex _mutex; +}; + +class SimpleSentinel { +public: + SimpleSentinel(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role); + + SimpleSentinel() = default; + + SimpleSentinel(const SimpleSentinel &) = default; + SimpleSentinel& operator=(const SimpleSentinel &) = default; + + SimpleSentinel(SimpleSentinel &&) = default; + SimpleSentinel& operator=(SimpleSentinel &&) = default; + + ~SimpleSentinel() = default; + + explicit operator bool() const { + return bool(_sentinel); + } + + Connection create(const ConnectionOptions &opts); + +private: + std::shared_ptr _sentinel; + + std::string _master_name; + + Role _role = Role::MASTER; +}; + +class StopIterError : public Error { +public: + StopIterError() : Error("StopIterError") {} + + StopIterError(const StopIterError &) = default; + StopIterError& operator=(const StopIterError &) = default; + + StopIterError(StopIterError &&) = default; + StopIterError& operator=(StopIterError &&) = default; + + virtual ~StopIterError() = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SENTINEL_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards.h new file mode 100644 index 000000000..a0593acbc --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards.h @@ -0,0 +1,115 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_H + +#include +#include +#include "errors.h" + +namespace sw { + +namespace redis { + +using Slot = std::size_t; + +struct SlotRange { + Slot min; + Slot max; +}; + +inline bool operator<(const SlotRange &lhs, const SlotRange &rhs) { + return lhs.max < rhs.max; +} + +struct Node { + std::string host; + int port; +}; + +inline bool operator==(const Node &lhs, const Node &rhs) { + return lhs.host == rhs.host && lhs.port == rhs.port; +} + +struct NodeHash { + std::size_t operator()(const Node &node) const noexcept { + auto host_hash = std::hash{}(node.host); + auto port_hash = std::hash{}(node.port); + return host_hash ^ (port_hash << 1); + } +}; + +using Shards = std::map; + +class RedirectionError : public ReplyError { +public: + RedirectionError(const std::string &msg); + + RedirectionError(const RedirectionError &) = default; + RedirectionError& operator=(const RedirectionError &) = default; + + RedirectionError(RedirectionError &&) = default; + RedirectionError& operator=(RedirectionError &&) = default; + + virtual ~RedirectionError() = default; + + Slot slot() const { + return _slot; + } + + const Node& node() const { + return _node; + } + +private: + std::pair _parse_error(const std::string &msg) const; + + Slot _slot = 0; + Node _node; +}; + +class MovedError : public RedirectionError { +public: + explicit MovedError(const std::string &msg) : RedirectionError(msg) {} + + MovedError(const MovedError &) = default; + MovedError& operator=(const MovedError &) = default; + + MovedError(MovedError &&) = default; + MovedError& operator=(MovedError &&) = default; + + virtual ~MovedError() = default; +}; + +class AskError : public RedirectionError { +public: + explicit AskError(const std::string &msg) : RedirectionError(msg) {} + + AskError(const AskError &) = default; + AskError& operator=(const AskError &) = default; + + AskError(AskError &&) = default; + AskError& operator=(AskError &&) = default; + + virtual ~AskError() = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards_pool.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards_pool.h new file mode 100644 index 000000000..1184806e9 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards_pool.h @@ -0,0 +1,137 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H + +#include +#include +#include +#include +#include +#include "reply.h" +#include "connection_pool.h" +#include "shards.h" + +namespace sw { + +namespace redis { + +using ConnectionPoolSPtr = std::shared_ptr; + +class GuardedConnection { +public: + GuardedConnection(const ConnectionPoolSPtr &pool) : _pool(pool), + _connection(_pool->fetch()) { + assert(!_connection.broken()); + } + + GuardedConnection(const GuardedConnection &) = delete; + GuardedConnection& operator=(const GuardedConnection &) = delete; + + GuardedConnection(GuardedConnection &&) = default; + GuardedConnection& operator=(GuardedConnection &&) = default; + + ~GuardedConnection() { + _pool->release(std::move(_connection)); + } + + Connection& connection() { + return _connection; + } + +private: + ConnectionPoolSPtr _pool; + Connection _connection; +}; + +class ShardsPool { +public: + ShardsPool() = default; + + ShardsPool(const ShardsPool &that) = delete; + ShardsPool& operator=(const ShardsPool &that) = delete; + + ShardsPool(ShardsPool &&that); + ShardsPool& operator=(ShardsPool &&that); + + ~ShardsPool() = default; + + ShardsPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + // Fetch a connection by key. + GuardedConnection fetch(const StringView &key); + + // Randomly pick a connection. + GuardedConnection fetch(); + + // Fetch a connection by node. + GuardedConnection fetch(const Node &node); + + void update(); + + ConnectionOptions connection_options(const StringView &key); + + ConnectionOptions connection_options(); + +private: + void _move(ShardsPool &&that); + + void _init_pool(const Shards &shards); + + Shards _cluster_slots(Connection &connection) const; + + ReplyUPtr _cluster_slots_command(Connection &connection) const; + + Shards _parse_reply(redisReply &reply) const; + + std::pair _parse_slot_info(redisReply &reply) const; + + // Get slot by key. + std::size_t _slot(const StringView &key) const; + + // Randomly pick a slot. + std::size_t _slot() const; + + ConnectionPoolSPtr& _get_pool(Slot slot); + + GuardedConnection _fetch(Slot slot); + + ConnectionOptions _connection_options(Slot slot); + + using NodeMap = std::unordered_map; + + NodeMap::iterator _add_node(const Node &node); + + ConnectionPoolOptions _pool_opts; + + ConnectionOptions _connection_opts; + + Shards _shards; + + NodeMap _pools; + + std::mutex _mutex; + + static const std::size_t SHARDS = 16383; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/subscriber.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/subscriber.h new file mode 100644 index 000000000..8b7c5cfb4 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/subscriber.h @@ -0,0 +1,231 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H +#define SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H + +#include +#include +#include +#include "connection.h" +#include "reply.h" +#include "command.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +// @NOTE: Subscriber is NOT thread-safe. +// Subscriber uses callbacks to handle messages. There are 6 kinds of messages: +// 1) MESSAGE: message sent to a channel. +// 2) PMESSAGE: message sent to channels of a given pattern. +// 3) SUBSCRIBE: meta message sent when we successfully subscribe to a channel. +// 4) UNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel. +// 5) PSUBSCRIBE: meta message sent when we successfully subscribe to a channel pattern. +// 6) PUNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel pattern. +// +// Use Subscriber::on_message(MsgCallback) to set the callback function for message of +// *MESSAGE* type, and the callback interface is: +// void (std::string channel, std::string msg) +// +// Use Subscriber::on_pmessage(PatternMsgCallback) to set the callback function for message of +// *PMESSAGE* type, and the callback interface is: +// void (std::string pattern, std::string channel, std::string msg) +// +// Messages of other types are called *META MESSAGE*, they have the same callback interface. +// Use Subscriber::on_meta(MetaCallback) to set the callback function: +// void (Subscriber::MsgType type, OptionalString channel, long long num) +// +// NOTE: If we haven't subscribe/psubscribe to any channel/pattern, and try to +// unsubscribe/punsubscribe without any parameter, i.e. unsubscribe/punsubscribe all +// channels/patterns, *channel* will be null. So the second parameter of meta callback +// is of type *OptionalString*. +// +// All these callback interfaces pass std::string by value, and you can take their ownership +// (i.e. std::move) safely. +// +// If you don't set callback for a specific kind of message, Subscriber::consume() will +// receive the message, and ignore it, i.e. no callback will be called. +class Subscriber { +public: + Subscriber(const Subscriber &) = delete; + Subscriber& operator=(const Subscriber &) = delete; + + Subscriber(Subscriber &&) = default; + Subscriber& operator=(Subscriber &&) = default; + + ~Subscriber() = default; + + enum class MsgType { + SUBSCRIBE, + UNSUBSCRIBE, + PSUBSCRIBE, + PUNSUBSCRIBE, + MESSAGE, + PMESSAGE + }; + + template + void on_message(MsgCb msg_callback); + + template + void on_pmessage(PMsgCb pmsg_callback); + + template + void on_meta(MetaCb meta_callback); + + void subscribe(const StringView &channel); + + template + void subscribe(Input first, Input last); + + template + void subscribe(std::initializer_list channels) { + subscribe(channels.begin(), channels.end()); + } + + void unsubscribe(); + + void unsubscribe(const StringView &channel); + + template + void unsubscribe(Input first, Input last); + + template + void unsubscribe(std::initializer_list channels) { + unsubscribe(channels.begin(), channels.end()); + } + + void psubscribe(const StringView &pattern); + + template + void psubscribe(Input first, Input last); + + template + void psubscribe(std::initializer_list channels) { + psubscribe(channels.begin(), channels.end()); + } + + void punsubscribe(); + + void punsubscribe(const StringView &channel); + + template + void punsubscribe(Input first, Input last); + + template + void punsubscribe(std::initializer_list channels) { + punsubscribe(channels.begin(), channels.end()); + } + + void consume(); + +private: + friend class Redis; + + friend class RedisCluster; + + explicit Subscriber(Connection connection); + + MsgType _msg_type(redisReply *reply) const; + + void _check_connection(); + + void _handle_message(redisReply &reply); + + void _handle_pmessage(redisReply &reply); + + void _handle_meta(MsgType type, redisReply &reply); + + using MsgCallback = std::function; + + using PatternMsgCallback = std::function; + + using MetaCallback = std::function; + + using TypeIndex = std::unordered_map; + static const TypeIndex _msg_type_index; + + Connection _connection; + + MsgCallback _msg_callback = nullptr; + + PatternMsgCallback _pmsg_callback = nullptr; + + MetaCallback _meta_callback = nullptr; +}; + +template +void Subscriber::on_message(MsgCb msg_callback) { + _msg_callback = msg_callback; +} + +template +void Subscriber::on_pmessage(PMsgCb pmsg_callback) { + _pmsg_callback = pmsg_callback; +} + +template +void Subscriber::on_meta(MetaCb meta_callback) { + _meta_callback = meta_callback; +} + +template +void Subscriber::subscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::subscribe_range(_connection, first, last); +} + +template +void Subscriber::unsubscribe(Input first, Input last) { + _check_connection(); + + cmd::unsubscribe_range(_connection, first, last); +} + +template +void Subscriber::psubscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::psubscribe_range(_connection, first, last); +} + +template +void Subscriber::punsubscribe(Input first, Input last) { + _check_connection(); + + cmd::punsubscribe_range(_connection, first, last); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/transaction.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/transaction.h new file mode 100644 index 000000000..f19f24889 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/transaction.h @@ -0,0 +1,77 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TRANSACTION_H +#define SEWENEW_REDISPLUSPLUS_TRANSACTION_H + +#include +#include +#include "connection.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class TransactionImpl { +public: + explicit TransactionImpl(bool piped) : _piped(piped) {} + + template + void command(Connection &connection, Cmd cmd, Args &&...args); + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t cmd_num); + +private: + void _open_transaction(Connection &connection); + + void _close_transaction(); + + void _get_queued_reply(Connection &connection); + + void _get_queued_replies(Connection &connection, std::size_t cmd_num); + + std::vector _exec(Connection &connection); + + void _discard(Connection &connection); + + bool _in_transaction = false; + + bool _piped; +}; + +template +void TransactionImpl::command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + if (!_in_transaction) { + _open_transaction(connection); + } + + cmd(connection, std::forward(args)...); + + if (!_piped) { + _get_queued_reply(connection); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TRANSACTION_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/utils.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/utils.h new file mode 100644 index 000000000..e29e64e14 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/utils.h @@ -0,0 +1,269 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed 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. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_UTILS_H +#define SEWENEW_REDISPLUSPLUS_UTILS_H + +#include +#include +#include + +namespace sw { + +namespace redis { + +// By now, not all compilers support std::string_view, +// so we make our own implementation. +class StringView { +public: + constexpr StringView() noexcept = default; + + constexpr StringView(const char *data, std::size_t size) : _data(data), _size(size) {} + + StringView(const char *data) : _data(data), _size(std::strlen(data)) {} + + StringView(const std::string &str) : _data(str.data()), _size(str.size()) {} + + constexpr StringView(const StringView &) noexcept = default; + + StringView& operator=(const StringView &) noexcept = default; + + constexpr const char* data() const noexcept { + return _data; + } + + constexpr std::size_t size() const noexcept { + return _size; + } + +private: + const char *_data = nullptr; + std::size_t _size = 0; +}; + +template +class Optional { +public: + Optional() = default; + + Optional(const Optional &) = default; + Optional& operator=(const Optional &) = default; + + Optional(Optional &&) = default; + Optional& operator=(Optional &&) = default; + + ~Optional() = default; + + template + explicit Optional(Args &&...args) : _value(true, T(std::forward(args)...)) {} + + explicit operator bool() const { + return _value.first; + } + + T& value() { + return _value.second; + } + + const T& value() const { + return _value.second; + } + + T* operator->() { + return &(_value.second); + } + + const T* operator->() const { + return &(_value.second); + } + + T& operator*() { + return _value.second; + } + + const T& operator*() const { + return _value.second; + } + +private: + std::pair _value; +}; + +using OptionalString = Optional; + +using OptionalLongLong = Optional; + +using OptionalDouble = Optional; + +using OptionalStringPair = Optional>; + +template +struct IsKvPair : std::false_type {}; + +template +struct IsKvPair> : std::true_type {}; + +template +using Void = void; + +template > +struct IsInserter : std::false_type {}; + +template +//struct IsInserter> : std::true_type {}; +struct IsInserter::value>::type> + : std::true_type {}; + +template > +struct IterType { + using type = typename std::iterator_traits::value_type; +}; + +template +//struct IterType> { +struct IterType::value>::type> { + typename std::enable_if::value>::type> { + using type = typename std::decay::type; +}; + +template > +struct IsIter : std::false_type {}; + +template +struct IsIter::value>::type> : std::true_type {}; + +template +//struct IsIter::iterator_category>> +struct IsIter::value_type>::value>::type> + : std::integral_constant::value> {}; + +template +struct IsKvPairIter : IsKvPair::type> {}; + +template +struct TupleWithType : std::false_type {}; + +template +struct TupleWithType> : std::false_type {}; + +template +struct TupleWithType> : TupleWithType> {}; + +template +struct TupleWithType> : std::true_type {}; + +template +struct IndexSequence {}; + +template +struct MakeIndexSequence : MakeIndexSequence {}; + +template +struct MakeIndexSequence<0, Is...> : IndexSequence {}; + +// NthType and NthValue are taken from +// https://stackoverflow.com/questions/14261183 +template +struct NthType {}; + +template +struct NthType<0, Arg, Args...> { + using type = Arg; +}; + +template +struct NthType { + using type = typename NthType::type; +}; + +template +struct LastType { + using type = typename NthType::type; +}; + +struct InvalidLastType {}; + +template <> +struct LastType<> { + using type = InvalidLastType; +}; + +template +auto NthValue(Arg &&arg, Args &&...) + -> typename std::enable_if<(I == 0), decltype(std::forward(arg))>::type { + return std::forward(arg); +} + +template +auto NthValue(Arg &&, Args &&...args) + -> typename std::enable_if<(I > 0), + decltype(std::forward::type>( + std::declval::type>()))>::type { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template +auto LastValue(Args &&...args) + -> decltype(std::forward::type>( + std::declval::type>())) { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template > +struct HasPushBack : std::false_type {}; + +template +struct HasPushBack().push_back(std::declval()) + )>::value>::type> : std::true_type {}; + +template > +struct HasInsert : std::false_type {}; + +template +struct HasInsert().insert(std::declval(), + std::declval())), + typename T::iterator>::value>::type> : std::true_type {}; + +template +struct IsSequenceContainer + : std::integral_constant::value + && !std::is_same::type, std::string>::value> {}; + +template +struct IsAssociativeContainer + : std::integral_constant::value && !HasPushBack::value> {}; + +uint16_t crc16(const char *buf, int len); + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_UTILS_H