Merge branch 'unstable' into 7.2 to prepare for 7.2.0 GA
This commit is contained in:
commit
941cdc2759
4
deps/README.md
vendored
4
deps/README.md
vendored
@ -63,6 +63,10 @@ Hiredis
|
||||
|
||||
Hiredis is used by Sentinel, `redis-cli` and `redis-benchmark`. Like Redis, uses the SDS string library, but not necessarily the same version. In order to avoid conflicts, this version has all SDS identifiers prefixed by `hi`.
|
||||
|
||||
1. `git subtree pull --prefix deps/hiredis https://github.com/redis/hiredis.git <version-tag> --squash`<br>
|
||||
This should hopefully merge the local changes into the new version.
|
||||
2. Conflicts will arise (due to our changes) you'll need to resolve them and commit.
|
||||
|
||||
Linenoise
|
||||
---
|
||||
|
||||
|
4
deps/hiredis/.github/workflows/build.yml
vendored
4
deps/hiredis/.github/workflows/build.yml
vendored
@ -133,8 +133,8 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install openssl redis@6.2
|
||||
brew link redis@6.2 --force
|
||||
brew install openssl redis@7.0
|
||||
brew link redis@7.0 --force
|
||||
|
||||
- name: Build hiredis
|
||||
run: USE_SSL=1 make
|
||||
|
2
deps/hiredis/.github/workflows/test.yml
vendored
2
deps/hiredis/.github/workflows/test.yml
vendored
@ -60,7 +60,7 @@ jobs:
|
||||
steps:
|
||||
- name: Install qemu
|
||||
if: matrix.emulator
|
||||
run: sudo apt-get install -y qemu-user
|
||||
run: sudo apt-get update && sudo apt-get install -y qemu-user
|
||||
- name: Install platform toolset
|
||||
if: matrix.toolset
|
||||
run: sudo apt-get install -y gcc-${{matrix.toolset}}
|
||||
|
57
deps/hiredis/CHANGELOG.md
vendored
57
deps/hiredis/CHANGELOG.md
vendored
@ -1,12 +1,63 @@
|
||||
## [1.2.0](https://github.com/redis/hiredis/tree/v1.2.0) - (2023-06-04)
|
||||
|
||||
Announcing Hiredis v1.2.0 with with new adapters, and a great many bug fixes.
|
||||
|
||||
## 🚀 New Features
|
||||
|
||||
- Add sdevent adapter @Oipo (#1144)
|
||||
- Allow specifying the keepalive interval @michael-grunder (#1168)
|
||||
- Add RedisModule adapter @tezc (#1182)
|
||||
- Helper for setting TCP_USER_TIMEOUT socket option @zuiderkwast (#1188)
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
- Fix a typo in b6a052f. @yossigo (#1190)
|
||||
- Fix wincrypt symbols conflict @hudayou (#1151)
|
||||
- Don't attempt to set a timeout if we are in an error state. @michael-grunder (#1180)
|
||||
- Accept -nan per the RESP3 spec recommendation. @michael-grunder (#1178)
|
||||
- Fix colliding option values @zuiderkwast (#1172)
|
||||
- Ensure functionality without `_MSC_VER` definition @windyakin (#1194)
|
||||
|
||||
## 🧰 Maintenance
|
||||
|
||||
- Add a test for the TCP_USER_TIMEOUT option. @michael-grunder (#1192)
|
||||
- Add -Werror as a default. @yossigo (#1193)
|
||||
- CI: Update homebrew Redis version. @yossigo (#1191)
|
||||
- Fix typo in makefile. @michael-grunder (#1179)
|
||||
- Write a version file for the CMake package @Neverlord (#1165)
|
||||
- CMakeLists.txt: respect BUILD_SHARED_LIBS @ffontaine (#1147)
|
||||
- Cmake static or shared @autoantwort (#1160)
|
||||
- fix typo @tillkruss (#1153)
|
||||
- Add a test ensuring we don't clobber connection error. @michael-grunder (#1181)
|
||||
- Search for openssl on macOS @michael-grunder (#1169)
|
||||
|
||||
|
||||
## Contributors
|
||||
We'd like to thank all the contributors who worked on this release!
|
||||
|
||||
<a href="https://github.com/neverlord"><img src="https://github.com/neverlord.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/Oipo"><img src="https://github.com/Oipo.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/autoantwort"><img src="https://github.com/autoantwort.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/ffontaine"><img src="https://github.com/ffontaine.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/hudayou"><img src="https://github.com/hudayou.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/michael-grunder"><img src="https://github.com/michael-grunder.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/postgraph"><img src="https://github.com/postgraph.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/tezc"><img src="https://github.com/tezc.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/tillkruss"><img src="https://github.com/tillkruss.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/vityafx"><img src="https://github.com/vityafx.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/windyakin"><img src="https://github.com/windyakin.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/yossigo"><img src="https://github.com/yossigo.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/zuiderkwast"><img src="https://github.com/zuiderkwast.png" width="32" height="32"></a>
|
||||
|
||||
## [1.1.0](https://github.com/redis/hiredis/tree/v1.1.0) - (2022-11-15)
|
||||
|
||||
Announcing Hiredis v1.1.0 GA with better SSL convenience, new async adapters and a great many bug fixes.
|
||||
|
||||
**NOTE**: Hiredis can now return `nan` in addition to `-inf` and `inf` when returning a `REDIS_REPLY_DOUBLE`.
|
||||
**NOTE**: Hiredis can now return `nan` in addition to `-inf` and `inf` when returning a `REDIS_REPLY_DOUBLE`.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
- Add support for nan in RESP3 double [@filipecosta90](https://github.com/filipecosta90)
|
||||
- Add support for nan in RESP3 double [@filipecosta90](https://github.com/filipecosta90)
|
||||
([\#1133](https://github.com/redis/hiredis/pull/1133))
|
||||
|
||||
## 🧰 Maintenance
|
||||
@ -14,7 +65,7 @@ Announcing Hiredis v1.1.0 GA with better SSL convenience, new async adapters and
|
||||
- Add an example that calls redisCommandArgv [@michael-grunder](https://github.com/michael-grunder)
|
||||
([\#1140](https://github.com/redis/hiredis/pull/1140))
|
||||
- fix flag reference [@pata00](https://github.com/pata00) ([\#1136](https://github.com/redis/hiredis/pull/1136))
|
||||
- Make freeing a NULL redisAsyncContext a no op. [@michael-grunder](https://github.com/michael-grunder)
|
||||
- Make freeing a NULL redisAsyncContext a no op. [@michael-grunder](https://github.com/michael-grunder)
|
||||
([\#1135](https://github.com/redis/hiredis/pull/1135))
|
||||
- CI updates ([@bjosv](https://github.com/redis/bjosv) ([\#1139](https://github.com/redis/hiredis/pull/1139))
|
||||
|
||||
|
6
deps/hiredis/Makefile
vendored
6
deps/hiredis/Makefile
vendored
@ -39,7 +39,7 @@ export REDIS_TEST_CONFIG
|
||||
CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
|
||||
CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
|
||||
OPTIMIZATION?=-O3
|
||||
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers
|
||||
WARNINGS=-Wall -Wextra -Werror -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers
|
||||
DEBUG_FLAGS?= -g -ggdb
|
||||
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) $(PLATFORM_FLAGS)
|
||||
REAL_LDFLAGS=$(LDFLAGS)
|
||||
@ -311,7 +311,7 @@ install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) $(SSL_INSTALL)
|
||||
$(INSTALL) hiredis.h async.h read.h sds.h alloc.h sockcompat.h $(INSTALL_INCLUDE_PATH)
|
||||
$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters
|
||||
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)
|
||||
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
|
||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
||||
$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
||||
@ -320,7 +320,7 @@ install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
|
||||
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
|
||||
$(INSTALL) hiredis_ssl.h $(INSTALL_INCLUDE_PATH)
|
||||
$(INSTALL) $(SSL_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIBNAME)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIBNAME) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIB_MAJOR_NAME)
|
||||
$(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH)
|
||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
||||
$(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
||||
|
4
deps/hiredis/hiredis.c
vendored
4
deps/hiredis/hiredis.c
vendored
@ -392,12 +392,12 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++;
|
||||
|
||||
/* Field width */
|
||||
while (*_p != '\0' && isdigit(*_p)) _p++;
|
||||
while (*_p != '\0' && isdigit((int) *_p)) _p++;
|
||||
|
||||
/* Precision */
|
||||
if (*_p == '.') {
|
||||
_p++;
|
||||
while (*_p != '\0' && isdigit(*_p)) _p++;
|
||||
while (*_p != '\0' && isdigit((int) *_p)) _p++;
|
||||
}
|
||||
|
||||
/* Copy va_list before consuming with va_arg */
|
||||
|
6
deps/hiredis/hiredis.h
vendored
6
deps/hiredis/hiredis.h
vendored
@ -46,9 +46,9 @@ typedef long long ssize_t;
|
||||
#include "alloc.h" /* for allocation wrappers */
|
||||
|
||||
#define HIREDIS_MAJOR 1
|
||||
#define HIREDIS_MINOR 1
|
||||
#define HIREDIS_PATCH 1
|
||||
#define HIREDIS_SONAME 1.1.1-dev
|
||||
#define HIREDIS_MINOR 2
|
||||
#define HIREDIS_PATCH 0
|
||||
#define HIREDIS_SONAME 1.1.0
|
||||
|
||||
/* Connection type can be blocking or non-blocking and is set in the
|
||||
* least significant bit of the flags field in redisContext. */
|
||||
|
1
deps/hiredis/net.c
vendored
1
deps/hiredis/net.c
vendored
@ -234,6 +234,7 @@ int redisContextSetTcpUserTimeout(redisContext *c, unsigned int timeout) {
|
||||
res = setsockopt(c->fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout));
|
||||
#else
|
||||
res = -1;
|
||||
errno = ENOTSUP;
|
||||
(void)timeout;
|
||||
#endif
|
||||
if (res == -1) {
|
||||
|
10
deps/hiredis/sds.c
vendored
10
deps/hiredis/sds.c
vendored
@ -948,7 +948,7 @@ hisds *hi_sdssplitargs(const char *line, int *argc) {
|
||||
*argc = 0;
|
||||
while(1) {
|
||||
/* skip blanks */
|
||||
while(*p && isspace(*p)) p++;
|
||||
while(*p && isspace((int) *p)) p++;
|
||||
if (*p) {
|
||||
/* get a token */
|
||||
int inq=0; /* set to 1 if we are in "quotes" */
|
||||
@ -959,8 +959,8 @@ hisds *hi_sdssplitargs(const char *line, int *argc) {
|
||||
while(!done) {
|
||||
if (inq) {
|
||||
if (*p == '\\' && *(p+1) == 'x' &&
|
||||
isxdigit(*(p+2)) &&
|
||||
isxdigit(*(p+3)))
|
||||
isxdigit((int) *(p+2)) &&
|
||||
isxdigit((int) *(p+3)))
|
||||
{
|
||||
unsigned char byte;
|
||||
|
||||
@ -984,7 +984,7 @@ hisds *hi_sdssplitargs(const char *line, int *argc) {
|
||||
} else if (*p == '"') {
|
||||
/* closing quote must be followed by a space or
|
||||
* nothing at all. */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
if (*(p+1) && !isspace((int) *(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
/* unterminated quotes */
|
||||
@ -999,7 +999,7 @@ hisds *hi_sdssplitargs(const char *line, int *argc) {
|
||||
} else if (*p == '\'') {
|
||||
/* closing quote must be followed by a space or
|
||||
* nothing at all. */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
if (*(p+1) && !isspace((int) *(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
/* unterminated quotes */
|
||||
|
20
deps/hiredis/ssl.c
vendored
20
deps/hiredis/ssl.c
vendored
@ -59,6 +59,8 @@
|
||||
#include "async_private.h"
|
||||
#include "hiredis_ssl.h"
|
||||
|
||||
#define OPENSSL_1_1_0 0x10100000L
|
||||
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
struct redisSSLContext {
|
||||
@ -100,7 +102,7 @@ redisContextFuncs redisContextSSLFuncs;
|
||||
* Note that this is only required for OpenSSL < 1.1.0.
|
||||
*/
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
#if OPENSSL_VERSION_NUMBER < OPENSSL_1_1_0
|
||||
#define HIREDIS_USE_CRYPTO_LOCKS
|
||||
#endif
|
||||
|
||||
@ -256,13 +258,25 @@ redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options, redi
|
||||
if (ctx == NULL)
|
||||
goto error;
|
||||
|
||||
ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
const SSL_METHOD *ssl_method;
|
||||
#if OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
|
||||
ssl_method = TLS_client_method();
|
||||
#else
|
||||
ssl_method = SSLv23_client_method();
|
||||
#endif
|
||||
|
||||
ctx->ssl_ctx = SSL_CTX_new(ssl_method);
|
||||
if (!ctx->ssl_ctx) {
|
||||
if (error) *error = REDIS_SSL_CTX_CREATE_FAILED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
#if OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
|
||||
SSL_CTX_set_min_proto_version(ctx->ssl_ctx, TLS1_2_VERSION);
|
||||
#else
|
||||
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
|
||||
#endif
|
||||
|
||||
SSL_CTX_set_verify(ctx->ssl_ctx, options->verify_mode, NULL);
|
||||
|
||||
if ((cert_filename != NULL && private_key_filename == NULL) ||
|
||||
|
16
deps/hiredis/test.c
vendored
16
deps/hiredis/test.c
vendored
@ -78,7 +78,7 @@ static int tests = 0, fails = 0, skips = 0;
|
||||
|
||||
static void millisleep(int ms)
|
||||
{
|
||||
#if _MSC_VER
|
||||
#ifdef _MSC_VER
|
||||
Sleep(ms);
|
||||
#else
|
||||
usleep(ms*1000);
|
||||
@ -409,10 +409,19 @@ static void test_tcp_options(struct config cfg) {
|
||||
redisContext *c;
|
||||
|
||||
c = do_connect(cfg);
|
||||
|
||||
test("We can enable TCP_KEEPALIVE: ");
|
||||
test_cond(redisEnableKeepAlive(c) == REDIS_OK);
|
||||
|
||||
disconnect(c, 0);
|
||||
#ifdef TCP_USER_TIMEOUT
|
||||
test("We can set TCP_USER_TIMEOUT: ");
|
||||
test_cond(redisSetTcpUserTimeout(c, 100) == REDIS_OK);
|
||||
#else
|
||||
test("Setting TCP_USER_TIMEOUT errors when unsupported: ");
|
||||
test_cond(redisSetTcpUserTimeout(c, 100) == REDIS_ERR && c->err == REDIS_ERR_IO);
|
||||
#endif
|
||||
|
||||
redisFree(c);
|
||||
}
|
||||
|
||||
static void test_reply_reader(void) {
|
||||
@ -1567,6 +1576,9 @@ static void test_throughput(struct config config) {
|
||||
// }
|
||||
|
||||
#ifdef HIREDIS_TEST_ASYNC
|
||||
|
||||
#pragma GCC diagnostic ignored "-Woverlength-strings" /* required on gcc 4.8.x due to assert statements */
|
||||
|
||||
struct event_base *base;
|
||||
|
||||
typedef struct TestState {
|
||||
|
3
deps/lua/src/lua_cjson.c
vendored
3
deps/lua/src/lua_cjson.c
vendored
@ -464,9 +464,8 @@ static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *js
|
||||
static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
|
||||
{
|
||||
const char *escstr;
|
||||
int i;
|
||||
const char *str;
|
||||
size_t len;
|
||||
size_t i, len;
|
||||
|
||||
str = lua_tolstring(l, lindex, &len);
|
||||
|
||||
|
3
deps/lua/src/strbuf.c
vendored
3
deps/lua/src/strbuf.c
vendored
@ -176,8 +176,7 @@ void strbuf_resize(strbuf_t *s, size_t len)
|
||||
|
||||
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
{
|
||||
int i;
|
||||
size_t space;
|
||||
size_t i, space;
|
||||
|
||||
space = strbuf_empty_length(s);
|
||||
|
||||
|
13
src/acl.c
13
src/acl.c
@ -563,7 +563,7 @@ void ACLSelectorRemoveCommandRule(aclSelector *selector, sds new_rule) {
|
||||
* as well if the command is removed. */
|
||||
char *rule_end = strchr(existing_rule, ' ');
|
||||
if (!rule_end) {
|
||||
/* This is the last rule, so it it to the end of the string. */
|
||||
/* This is the last rule, so move it to the end of the string. */
|
||||
rule_end = existing_rule + strlen(existing_rule);
|
||||
|
||||
/* This approach can leave a trailing space if the last rule is removed,
|
||||
@ -580,6 +580,8 @@ void ACLSelectorRemoveCommandRule(aclSelector *selector, sds new_rule) {
|
||||
/* Copy the remaining rules starting at the next rule to replace the rule to be
|
||||
* deleted, including the terminating NULL character. */
|
||||
memmove(copy_position, copy_end, strlen(copy_end) + 1);
|
||||
existing_rule = copy_position;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
existing_rule = copy_end;
|
||||
@ -1859,6 +1861,12 @@ int ACLCheckAllPerm(client *c, int *idxptr) {
|
||||
/* Check if the user's existing pub/sub clients violate the ACL pub/sub
|
||||
* permissions specified via the upcoming argument, and kill them if so. */
|
||||
void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
|
||||
/* Do nothing if there are no subscribers. */
|
||||
if (!dictSize(server.pubsub_patterns) &&
|
||||
!dictSize(server.pubsub_channels) &&
|
||||
!dictSize(server.pubsubshard_channels))
|
||||
return;
|
||||
|
||||
listIter li, lpi;
|
||||
listNode *ln, *lpn;
|
||||
robj *o;
|
||||
@ -1990,7 +1998,8 @@ sds *ACLMergeSelectorArguments(sds *argv, int argc, int *merged_argc, int *inval
|
||||
for (int j = 0; j < argc; j++) {
|
||||
char *op = argv[j];
|
||||
|
||||
if (op[0] == '(' && op[sdslen(op) - 1] != ')') {
|
||||
if (open_bracket_start == -1 &&
|
||||
(op[0] == '(' && op[sdslen(op) - 1] != ')')) {
|
||||
selector = sdsdup(argv[j]);
|
||||
open_bracket_start = j;
|
||||
continue;
|
||||
|
@ -5230,12 +5230,12 @@ sds clusterGenNodeDescription(client *c, clusterNode *node, int tls_primary) {
|
||||
if (sdslen(node->hostname) != 0) {
|
||||
ci = sdscatfmt(ci,",%s", node->hostname);
|
||||
}
|
||||
if (sdslen(node->hostname) == 0) {
|
||||
ci = sdscatfmt(ci,",", 1);
|
||||
}
|
||||
/* Don't expose aux fields to any clients yet but do allow them
|
||||
* to be persisted to nodes.conf */
|
||||
if (c == NULL) {
|
||||
if (sdslen(node->hostname) == 0) {
|
||||
ci = sdscatfmt(ci,",", 1);
|
||||
}
|
||||
for (int i = af_count-1; i >=0; i--) {
|
||||
if ((tls_primary && i == af_tls_port) || (!tls_primary && i == af_tcp_port)) {
|
||||
continue;
|
||||
@ -6294,6 +6294,7 @@ NULL
|
||||
} else if ((!strcasecmp(c->argv[1]->ptr,"slaves") ||
|
||||
!strcasecmp(c->argv[1]->ptr,"replicas")) && c->argc == 3) {
|
||||
/* CLUSTER SLAVES <NODE ID> */
|
||||
/* CLUSTER REPLICAS <NODE ID> */
|
||||
clusterNode *n = clusterLookupNode(c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
|
||||
int j;
|
||||
|
||||
|
@ -964,14 +964,14 @@ struct COMMAND_STRUCT CLUSTER_Subcommands[] = {
|
||||
{MAKE_CMD("myid","Returns the ID of a node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_MYID_History,0,CLUSTER_MYID_Tips,0,clusterCommand,2,CMD_STALE,0,CLUSTER_MYID_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("myshardid","Returns the shard ID of a node.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_MYSHARDID_History,0,CLUSTER_MYSHARDID_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_MYSHARDID_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("nodes","Returns the cluster configuration for a node.","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_NODES_History,0,CLUSTER_NODES_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_NODES_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("replicas","Lists the replica nodes of a master node.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_REPLICAS_History,0,CLUSTER_REPLICAS_Tips,1,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,CLUSTER_REPLICAS_Keyspecs,0,NULL,1),.args=CLUSTER_REPLICAS_Args},
|
||||
{MAKE_CMD("replicas","Lists the replica nodes of a master node.","O(N) where N is the number of replicas.","5.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_REPLICAS_History,0,CLUSTER_REPLICAS_Tips,1,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,CLUSTER_REPLICAS_Keyspecs,0,NULL,1),.args=CLUSTER_REPLICAS_Args},
|
||||
{MAKE_CMD("replicate","Configure a node as replica of a master node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_REPLICATE_History,0,CLUSTER_REPLICATE_Tips,0,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_REPLICATE_Keyspecs,0,NULL,1),.args=CLUSTER_REPLICATE_Args},
|
||||
{MAKE_CMD("reset","Resets a node.","O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_RESET_History,0,CLUSTER_RESET_Tips,0,clusterCommand,-2,CMD_ADMIN|CMD_STALE|CMD_NOSCRIPT,0,CLUSTER_RESET_Keyspecs,0,NULL,1),.args=CLUSTER_RESET_Args},
|
||||
{MAKE_CMD("saveconfig","Forces a node to save the cluster configuration to disk.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SAVECONFIG_History,0,CLUSTER_SAVECONFIG_Tips,0,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SAVECONFIG_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("set-config-epoch","Sets the configuration epoch for a new node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SET_CONFIG_EPOCH_History,0,CLUSTER_SET_CONFIG_EPOCH_Tips,0,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SET_CONFIG_EPOCH_Keyspecs,0,NULL,1),.args=CLUSTER_SET_CONFIG_EPOCH_Args},
|
||||
{MAKE_CMD("setslot","Binds a hash slot to a node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SETSLOT_History,0,CLUSTER_SETSLOT_Tips,0,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SETSLOT_Keyspecs,0,NULL,2),.args=CLUSTER_SETSLOT_Args},
|
||||
{MAKE_CMD("shards","Returns the mapping of cluster slots to shards.","O(N) where N is the total number of cluster nodes","7.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SHARDS_History,0,CLUSTER_SHARDS_Tips,1,clusterCommand,2,CMD_LOADING|CMD_STALE,0,CLUSTER_SHARDS_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("slaves","Lists the replica nodes of a master node.","O(1)","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER REPLICAS`","5.0.0","cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SLAVES_History,0,CLUSTER_SLAVES_Tips,1,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,CLUSTER_SLAVES_Keyspecs,0,NULL,1),.args=CLUSTER_SLAVES_Args},
|
||||
{MAKE_CMD("slaves","Lists the replica nodes of a master node.","O(N) where N is the number of replicas.","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER REPLICAS`","5.0.0","cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SLAVES_History,0,CLUSTER_SLAVES_Tips,1,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,CLUSTER_SLAVES_Keyspecs,0,NULL,1),.args=CLUSTER_SLAVES_Args},
|
||||
{MAKE_CMD("slots","Returns the mapping of cluster slots to nodes.","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER SHARDS`","7.0.0","cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SLOTS_History,2,CLUSTER_SLOTS_Tips,1,clusterCommand,2,CMD_LOADING|CMD_STALE,0,CLUSTER_SLOTS_Keyspecs,0,NULL,0)},
|
||||
{0}
|
||||
};
|
||||
@ -2328,6 +2328,7 @@ struct COMMAND_ARG PTTL_Args[] = {
|
||||
/* RANDOMKEY tips */
|
||||
const char *RANDOMKEY_Tips[] = {
|
||||
"request_policy:all_shards",
|
||||
"response_policy:special",
|
||||
"nondeterministic_output",
|
||||
};
|
||||
#endif
|
||||
@ -2437,6 +2438,7 @@ commandHistory SCAN_History[] = {
|
||||
const char *SCAN_Tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:special",
|
||||
"response_policy:special",
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -10263,10 +10265,7 @@ struct COMMAND_ARG MSET_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* MSETNX tips */
|
||||
const char *MSETNX_Tips[] = {
|
||||
"request_policy:multi_shard",
|
||||
"response_policy:agg_min",
|
||||
};
|
||||
#define MSETNX_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@ -10621,11 +10620,11 @@ struct COMMAND_STRUCT redisCommandTable[] = {
|
||||
{MAKE_CMD("pexpireat","Sets the expiration time of a key to a Unix milliseconds timestamp.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,PEXPIREAT_History,1,PEXPIREAT_Tips,0,pexpireatCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,PEXPIREAT_Keyspecs,1,NULL,3),.args=PEXPIREAT_Args},
|
||||
{MAKE_CMD("pexpiretime","Returns the expiration time of a key as a Unix milliseconds timestamp.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,PEXPIRETIME_History,0,PEXPIRETIME_Tips,0,pexpiretimeCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,PEXPIRETIME_Keyspecs,1,NULL,1),.args=PEXPIRETIME_Args},
|
||||
{MAKE_CMD("pttl","Returns the expiration time in milliseconds of a key.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,PTTL_History,1,PTTL_Tips,1,pttlCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,PTTL_Keyspecs,1,NULL,1),.args=PTTL_Args},
|
||||
{MAKE_CMD("randomkey","Returns a random key name from the database.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,RANDOMKEY_History,0,RANDOMKEY_Tips,2,randomkeyCommand,1,CMD_READONLY|CMD_TOUCHES_ARBITRARY_KEYS,ACL_CATEGORY_KEYSPACE,RANDOMKEY_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("randomkey","Returns a random key name from the database.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,RANDOMKEY_History,0,RANDOMKEY_Tips,3,randomkeyCommand,1,CMD_READONLY|CMD_TOUCHES_ARBITRARY_KEYS,ACL_CATEGORY_KEYSPACE,RANDOMKEY_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("rename","Renames a key and overwrites the destination.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,RENAME_History,0,RENAME_Tips,0,renameCommand,3,CMD_WRITE,ACL_CATEGORY_KEYSPACE,RENAME_Keyspecs,2,NULL,2),.args=RENAME_Args},
|
||||
{MAKE_CMD("renamenx","Renames a key only when the target key name doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,RENAMENX_History,1,RENAMENX_Tips,0,renamenxCommand,3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,RENAMENX_Keyspecs,2,NULL,2),.args=RENAMENX_Args},
|
||||
{MAKE_CMD("restore","Creates a key from the serialized representation of a value.","O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).","2.6.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,RESTORE_History,3,RESTORE_Tips,0,restoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,RESTORE_Keyspecs,1,NULL,7),.args=RESTORE_Args},
|
||||
{MAKE_CMD("scan","Iterates over the key names in the database.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,SCAN_History,1,SCAN_Tips,2,scanCommand,-2,CMD_READONLY|CMD_TOUCHES_ARBITRARY_KEYS,ACL_CATEGORY_KEYSPACE,SCAN_Keyspecs,0,NULL,4),.args=SCAN_Args},
|
||||
{MAKE_CMD("scan","Iterates over the key names in the database.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,SCAN_History,1,SCAN_Tips,3,scanCommand,-2,CMD_READONLY|CMD_TOUCHES_ARBITRARY_KEYS,ACL_CATEGORY_KEYSPACE,SCAN_Keyspecs,0,NULL,4),.args=SCAN_Args},
|
||||
{MAKE_CMD("sort","Sorts the elements in a list, a set, or a sorted set, optionally storing the result.","O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,SORT_History,0,SORT_Tips,0,sortCommand,-2,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_LIST|ACL_CATEGORY_DANGEROUS,SORT_Keyspecs,3,sortGetKeys,7),.args=SORT_Args},
|
||||
{MAKE_CMD("sort_ro","Returns the sorted elements of a list, a set, or a sorted set.","O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).","7.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,SORT_RO_History,0,SORT_RO_Tips,0,sortroCommand,-2,CMD_READONLY,ACL_CATEGORY_SET|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_LIST|ACL_CATEGORY_DANGEROUS,SORT_RO_Keyspecs,2,sortROGetKeys,6),.args=SORT_RO_Args},
|
||||
{MAKE_CMD("touch","Returns the number of existing keys out of those specified after updating the time they were last accessed.","O(N) where N is the number of keys that will be touched.","3.2.1",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,TOUCH_History,0,TOUCH_Tips,2,touchCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,TOUCH_Keyspecs,1,NULL,1),.args=TOUCH_Args},
|
||||
@ -10636,9 +10635,9 @@ struct COMMAND_STRUCT redisCommandTable[] = {
|
||||
{MAKE_CMD("waitaof","Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,WAITAOF_History,0,WAITAOF_Tips,2,waitaofCommand,4,CMD_NOSCRIPT,ACL_CATEGORY_CONNECTION,WAITAOF_Keyspecs,0,NULL,3),.args=WAITAOF_Args},
|
||||
/* geo */
|
||||
{MAKE_CMD("geoadd","Adds one or more members to a geospatial index. The key is created if it doesn't exist.","O(log(N)) for each item added, where N is the number of elements in the sorted set.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOADD_History,1,GEOADD_Tips,0,geoaddCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEOADD_Keyspecs,1,NULL,4),.args=GEOADD_Args},
|
||||
{MAKE_CMD("geodist","Returns the distance between two members of a geospatial index.","O(log(N))","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEODIST_History,0,GEODIST_Tips,0,geodistCommand,-4,CMD_READONLY,ACL_CATEGORY_GEO,GEODIST_Keyspecs,1,NULL,4),.args=GEODIST_Args},
|
||||
{MAKE_CMD("geohash","Returns members from a geospatial index as geohash strings.","O(log(N)) for each member requested, where N is the number of elements in the sorted set.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOHASH_History,0,GEOHASH_Tips,0,geohashCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,GEOHASH_Keyspecs,1,NULL,2),.args=GEOHASH_Args},
|
||||
{MAKE_CMD("geopos","Returns the longitude and latitude of members from a geospatial index.","O(N) where N is the number of members requested.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOPOS_History,0,GEOPOS_Tips,0,geoposCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,GEOPOS_Keyspecs,1,NULL,2),.args=GEOPOS_Args},
|
||||
{MAKE_CMD("geodist","Returns the distance between two members of a geospatial index.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEODIST_History,0,GEODIST_Tips,0,geodistCommand,-4,CMD_READONLY,ACL_CATEGORY_GEO,GEODIST_Keyspecs,1,NULL,4),.args=GEODIST_Args},
|
||||
{MAKE_CMD("geohash","Returns members from a geospatial index as geohash strings.","O(1) for each member requested.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOHASH_History,0,GEOHASH_Tips,0,geohashCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,GEOHASH_Keyspecs,1,NULL,2),.args=GEOHASH_Args},
|
||||
{MAKE_CMD("geopos","Returns the longitude and latitude of members from a geospatial index.","O(1) for each member requested.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOPOS_History,0,GEOPOS_Tips,0,geoposCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,GEOPOS_Keyspecs,1,NULL,2),.args=GEOPOS_Args},
|
||||
{MAKE_CMD("georadius","Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.0",CMD_DOC_DEPRECATED,"`GEOSEARCH` and `GEOSEARCHSTORE` with the `BYRADIUS` argument","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUS_History,2,GEORADIUS_Tips,0,georadiusCommand,-6,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEORADIUS_Keyspecs,3,georadiusGetKeys,11),.args=GEORADIUS_Args},
|
||||
{MAKE_CMD("georadiusbymember","Queries a geospatial index for members within a distance from a member, optionally stores the result.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.0",CMD_DOC_DEPRECATED,"`GEOSEARCH` and `GEOSEARCHSTORE` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_History,1,GEORADIUSBYMEMBER_Tips,0,georadiusbymemberCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEORADIUSBYMEMBER_Keyspecs,3,georadiusGetKeys,10),.args=GEORADIUSBYMEMBER_Args},
|
||||
{MAKE_CMD("georadiusbymember_ro","Returns members from a geospatial index that are within a distance from a member.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.10",CMD_DOC_DEPRECATED,"`GEOSEARCH` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_RO_History,0,GEORADIUSBYMEMBER_RO_Tips,0,georadiusbymemberroCommand,-5,CMD_READONLY,ACL_CATEGORY_GEO,GEORADIUSBYMEMBER_RO_Keyspecs,1,NULL,9),.args=GEORADIUSBYMEMBER_RO_Args},
|
||||
@ -10827,7 +10826,7 @@ struct COMMAND_STRUCT redisCommandTable[] = {
|
||||
{MAKE_CMD("lcs","Finds the longest common substring.","O(N*M) where N and M are the lengths of s1 and s2, respectively","7.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,LCS_History,0,LCS_Tips,0,lcsCommand,-3,CMD_READONLY,ACL_CATEGORY_STRING,LCS_Keyspecs,1,NULL,6),.args=LCS_Args},
|
||||
{MAKE_CMD("mget","Atomically returns the string values of one or more keys.","O(N) where N is the number of keys to retrieve.","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,MGET_History,0,MGET_Tips,1,mgetCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STRING,MGET_Keyspecs,1,NULL,1),.args=MGET_Args},
|
||||
{MAKE_CMD("mset","Atomically creates or modifies the string values of one or more keys.","O(N) where N is the number of keys to set.","1.0.1",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,MSET_History,0,MSET_Tips,2,msetCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,MSET_Keyspecs,1,NULL,1),.args=MSET_Args},
|
||||
{MAKE_CMD("msetnx","Atomically modifies the string values of one or more keys only when all keys don't exist.","O(N) where N is the number of keys to set.","1.0.1",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,MSETNX_History,0,MSETNX_Tips,2,msetnxCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,MSETNX_Keyspecs,1,NULL,1),.args=MSETNX_Args},
|
||||
{MAKE_CMD("msetnx","Atomically modifies the string values of one or more keys only when all keys don't exist.","O(N) where N is the number of keys to set.","1.0.1",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,MSETNX_History,0,MSETNX_Tips,0,msetnxCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,MSETNX_Keyspecs,1,NULL,1),.args=MSETNX_Args},
|
||||
{MAKE_CMD("psetex","Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.","O(1)","2.6.0",CMD_DOC_DEPRECATED,"`SET` with the `PX` argument","2.6.12","string",COMMAND_GROUP_STRING,PSETEX_History,0,PSETEX_Tips,0,psetexCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,PSETEX_Keyspecs,1,NULL,3),.args=PSETEX_Args},
|
||||
{MAKE_CMD("set","Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,SET_History,4,SET_Tips,0,setCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SET_Keyspecs,1,setGetKeys,5),.args=SET_Args},
|
||||
{MAKE_CMD("setex","Sets the string value and expiration time of a key. Creates the key if it doesn't exist.","O(1)","2.0.0",CMD_DOC_DEPRECATED,"`SET` with the `EX` argument","2.6.12","string",COMMAND_GROUP_STRING,SETEX_History,0,SETEX_Tips,0,setexCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SETEX_Keyspecs,1,NULL,3),.args=SETEX_Args},
|
||||
|
@ -19,7 +19,7 @@ typedef enum {
|
||||
#define CMD_ARG_MULTIPLE (1<<1)
|
||||
#define CMD_ARG_MULTIPLE_TOKEN (1<<2)
|
||||
|
||||
/* WARNING! This struct must match RedisModuleCommandArg */
|
||||
/* Must be compatible with RedisModuleCommandArg. See moduleCopyCommandArgs. */
|
||||
typedef struct redisCommandArg {
|
||||
const char *name;
|
||||
redisCommandArgType type;
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"REPLICAS": {
|
||||
"summary": "Lists the replica nodes of a master node.",
|
||||
"complexity": "O(1)",
|
||||
"complexity": "O(N) where N is the number of replicas.",
|
||||
"group": "cluster",
|
||||
"since": "5.0.0",
|
||||
"arity": 3,
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"SLAVES": {
|
||||
"summary": "Lists the replica nodes of a master node.",
|
||||
"complexity": "O(1)",
|
||||
"complexity": "O(N) where N is the number of replicas.",
|
||||
"group": "cluster",
|
||||
"since": "3.0.0",
|
||||
"arity": 3,
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"GEODIST": {
|
||||
"summary": "Returns the distance between two members of a geospatial index.",
|
||||
"complexity": "O(log(N))",
|
||||
"complexity": "O(1)",
|
||||
"group": "geo",
|
||||
"since": "3.2.0",
|
||||
"arity": -4,
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"GEOHASH": {
|
||||
"summary": "Returns members from a geospatial index as geohash strings.",
|
||||
"complexity": "O(log(N)) for each member requested, where N is the number of elements in the sorted set.",
|
||||
"complexity": "O(1) for each member requested.",
|
||||
"group": "geo",
|
||||
"since": "3.2.0",
|
||||
"arity": -2,
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"GEOPOS": {
|
||||
"summary": "Returns the longitude and latitude of members from a geospatial index.",
|
||||
"complexity": "O(N) where N is the number of members requested.",
|
||||
"complexity": "O(1) for each member requested.",
|
||||
"group": "geo",
|
||||
"since": "3.2.0",
|
||||
"arity": -2,
|
||||
|
@ -13,10 +13,6 @@
|
||||
"acl_categories": [
|
||||
"STRING"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:MULTI_SHARD",
|
||||
"RESPONSE_POLICY:AGG_MIN"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
|
@ -15,6 +15,7 @@
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_SHARDS",
|
||||
"RESPONSE_POLICY:SPECIAL",
|
||||
"NONDETERMINISTIC_OUTPUT"
|
||||
],
|
||||
"reply_schema": {
|
||||
|
@ -21,7 +21,8 @@
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:SPECIAL"
|
||||
"REQUEST_POLICY:SPECIAL",
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
|
@ -51,7 +51,6 @@
|
||||
#define REGISTRY_LOAD_CTX_NAME "__LIBRARY_CTX__"
|
||||
#define LIBRARY_API_NAME "__LIBRARY_API__"
|
||||
#define GLOBALS_API_NAME "__GLOBALS_API__"
|
||||
#define LOAD_TIMEOUT_MS 500
|
||||
|
||||
/* Lua engine ctx */
|
||||
typedef struct luaEngineCtx {
|
||||
@ -67,6 +66,7 @@ typedef struct luaFunctionCtx {
|
||||
typedef struct loadCtx {
|
||||
functionLibInfo *li;
|
||||
monotime start_time;
|
||||
size_t timeout;
|
||||
} loadCtx;
|
||||
|
||||
typedef struct registerFunctionArgs {
|
||||
@ -85,7 +85,7 @@ static void luaEngineLoadHook(lua_State *lua, lua_Debug *ar) {
|
||||
loadCtx *load_ctx = luaGetFromRegistry(lua, REGISTRY_LOAD_CTX_NAME);
|
||||
serverAssert(load_ctx); /* Only supported inside script invocation */
|
||||
uint64_t duration = elapsedMs(load_ctx->start_time);
|
||||
if (duration > LOAD_TIMEOUT_MS) {
|
||||
if (load_ctx->timeout > 0 && duration > load_ctx->timeout) {
|
||||
lua_sethook(lua, luaEngineLoadHook, LUA_MASKLINE, 0);
|
||||
|
||||
luaPushError(lua,"FUNCTION LOAD timeout");
|
||||
@ -100,7 +100,7 @@ static void luaEngineLoadHook(lua_State *lua, lua_Debug *ar) {
|
||||
*
|
||||
* Return NULL on compilation error and set the error to the err variable
|
||||
*/
|
||||
static int luaEngineCreate(void *engine_ctx, functionLibInfo *li, sds blob, sds *err) {
|
||||
static int luaEngineCreate(void *engine_ctx, functionLibInfo *li, sds blob, size_t timeout, sds *err) {
|
||||
int ret = C_ERR;
|
||||
luaEngineCtx *lua_engine_ctx = engine_ctx;
|
||||
lua_State *lua = lua_engine_ctx->lua;
|
||||
@ -124,6 +124,7 @@ static int luaEngineCreate(void *engine_ctx, functionLibInfo *li, sds blob, sds
|
||||
loadCtx load_ctx = {
|
||||
.li = li,
|
||||
.start_time = getMonotonicUs(),
|
||||
.timeout = timeout,
|
||||
};
|
||||
luaSaveOnRegistry(lua, REGISTRY_LOAD_CTX_NAME, &load_ctx);
|
||||
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include "adlist.h"
|
||||
#include "atomicvar.h"
|
||||
|
||||
#define LOAD_TIMEOUT_MS 500
|
||||
|
||||
typedef enum {
|
||||
restorePolicy_Flush, restorePolicy_Append, restorePolicy_Replace
|
||||
} restorePolicy;
|
||||
@ -961,7 +963,7 @@ void functionFreeLibMetaData(functionsLibMataData *md) {
|
||||
|
||||
/* Compile and save the given library, return the loaded library name on success
|
||||
* and NULL on failure. In case on failure the err out param is set with relevant error message */
|
||||
sds functionsCreateWithLibraryCtx(sds code, int replace, sds* err, functionsLibCtx *lib_ctx) {
|
||||
sds functionsCreateWithLibraryCtx(sds code, int replace, sds* err, functionsLibCtx *lib_ctx, size_t timeout) {
|
||||
dictIterator *iter = NULL;
|
||||
dictEntry *entry = NULL;
|
||||
functionLibInfo *new_li = NULL;
|
||||
@ -995,7 +997,7 @@ sds functionsCreateWithLibraryCtx(sds code, int replace, sds* err, functionsLibC
|
||||
}
|
||||
|
||||
new_li = engineLibraryCreate(md.name, ei, code);
|
||||
if (engine->create(engine->engine_ctx, new_li, md.code, err) != C_OK) {
|
||||
if (engine->create(engine->engine_ctx, new_li, md.code, timeout, err) != C_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -1063,7 +1065,11 @@ void functionLoadCommand(client *c) {
|
||||
robj *code = c->argv[argc_pos];
|
||||
sds err = NULL;
|
||||
sds library_name = NULL;
|
||||
if (!(library_name = functionsCreateWithLibraryCtx(code->ptr, replace, &err, curr_functions_lib_ctx)))
|
||||
size_t timeout = LOAD_TIMEOUT_MS;
|
||||
if (mustObeyClient(c)) {
|
||||
timeout = 0;
|
||||
}
|
||||
if (!(library_name = functionsCreateWithLibraryCtx(code->ptr, replace, &err, curr_functions_lib_ctx, timeout)))
|
||||
{
|
||||
addReplyErrorSds(c, err);
|
||||
return;
|
||||
|
@ -53,9 +53,14 @@ typedef struct engine {
|
||||
/* engine specific context */
|
||||
void *engine_ctx;
|
||||
|
||||
/* Create function callback, get the engine_ctx, and function code.
|
||||
/* Create function callback, get the engine_ctx, and function code
|
||||
* engine_ctx - opaque struct that was created on engine initialization
|
||||
* li - library information that need to be provided and when add functions
|
||||
* code - the library code
|
||||
* timeout - timeout for the library creation (0 for no timeout)
|
||||
* err - description of error (if occurred)
|
||||
* returns NULL on error and set sds to be the error message */
|
||||
int (*create)(void *engine_ctx, functionLibInfo *li, sds code, sds *err);
|
||||
int (*create)(void *engine_ctx, functionLibInfo *li, sds code, size_t timeout, sds *err);
|
||||
|
||||
/* Invoking a function, r_ctx is an opaque object (from engine POV).
|
||||
* The r_ctx should be used by the engine to interaction with Redis,
|
||||
@ -109,7 +114,7 @@ struct functionLibInfo {
|
||||
};
|
||||
|
||||
int functionsRegisterEngine(const char *engine_name, engine *engine_ctx);
|
||||
sds functionsCreateWithLibraryCtx(sds code, int replace, sds* err, functionsLibCtx *lib_ctx);
|
||||
sds functionsCreateWithLibraryCtx(sds code, int replace, sds* err, functionsLibCtx *lib_ctx, size_t timeout);
|
||||
unsigned long functionsMemory(void);
|
||||
unsigned long functionsMemoryOverhead(void);
|
||||
unsigned long functionsNum(void);
|
||||
|
@ -3808,7 +3808,7 @@ size_t getClientMemoryUsage(client *c, size_t *output_buffer_mem_usage) {
|
||||
* classes of clients.
|
||||
*
|
||||
* The function will return one of the following:
|
||||
* CLIENT_TYPE_NORMAL -> Normal client
|
||||
* CLIENT_TYPE_NORMAL -> Normal client, including MONITOR
|
||||
* CLIENT_TYPE_SLAVE -> Slave
|
||||
* CLIENT_TYPE_PUBSUB -> Client subscribed to Pub/Sub channels
|
||||
* CLIENT_TYPE_MASTER -> The client representing our replication master.
|
||||
|
@ -2981,7 +2981,7 @@ int rdbFunctionLoad(rio *rdb, int ver, functionsLibCtx* lib_ctx, int rdbflags, s
|
||||
|
||||
if (lib_ctx) {
|
||||
sds library_name = NULL;
|
||||
if (!(library_name = functionsCreateWithLibraryCtx(final_payload, rdbflags & RDBFLAGS_ALLOW_DUP, &error, lib_ctx))) {
|
||||
if (!(library_name = functionsCreateWithLibraryCtx(final_payload, rdbflags & RDBFLAGS_ALLOW_DUP, &error, lib_ctx, 0))) {
|
||||
if (!error) {
|
||||
error = sdsnew("Failed creating the library");
|
||||
}
|
||||
|
@ -2291,8 +2291,12 @@ static int cliReadReply(int output_raw_strings) {
|
||||
slot = atoi(s+1);
|
||||
s = strrchr(p+1,':'); /* MOVED 3999[P]127.0.0.1[S]6381 */
|
||||
*s = '\0';
|
||||
sdsfree(config.conn_info.hostip);
|
||||
config.conn_info.hostip = sdsnew(p+1);
|
||||
if (p+1 != s) {
|
||||
/* Host might be empty, like 'MOVED 3999 :6381', if endpoint type is unknown. Only update the
|
||||
* host if it's non-empty. */
|
||||
sdsfree(config.conn_info.hostip);
|
||||
config.conn_info.hostip = sdsnew(p+1);
|
||||
}
|
||||
config.conn_info.hostport = atoi(s+1);
|
||||
if (config.interactive)
|
||||
printf("-> Redirected to slot [%d] located at %s:%d\n",
|
||||
|
@ -610,6 +610,7 @@ void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv,
|
||||
while((ln = listNext(&li))) {
|
||||
client *monitor = ln->value;
|
||||
addReply(monitor,cmdobj);
|
||||
updateClientMemUsageAndBucket(monitor);
|
||||
}
|
||||
decrRefCount(cmdobj);
|
||||
}
|
||||
|
@ -917,11 +917,9 @@ void removeClientFromMemUsageBucket(client *c, int allow_eviction) {
|
||||
* together clients consuming about the same amount of memory and can quickly
|
||||
* free them in case we reach maxmemory-clients (client eviction).
|
||||
*
|
||||
* Note: This function filters clients of type monitor, master or replica regardless
|
||||
* Note: This function filters clients of type no-evict, master or replica regardless
|
||||
* of whether the eviction is enabled or not, so the memory usage we get from these
|
||||
* types of clients via the INFO command may be out of date. If someday we wanna
|
||||
* improve that to make monitors' memory usage more accurate, we need to re-add this
|
||||
* function call to `replicationFeedMonitors()`.
|
||||
* types of clients via the INFO command may be out of date.
|
||||
*
|
||||
* returns 1 if client eviction for this client is allowed, 0 otherwise.
|
||||
*/
|
||||
@ -4160,7 +4158,7 @@ int processCommand(client *c) {
|
||||
int flags = CMD_CALL_FULL;
|
||||
if (client_reprocessing_command) flags |= CMD_CALL_REPROCESSING;
|
||||
call(c,flags);
|
||||
if (listLength(server.ready_keys))
|
||||
if (listLength(server.ready_keys) && !isInsideYieldingLongCommand())
|
||||
handleClientsBlockedOnKeys();
|
||||
}
|
||||
|
||||
|
@ -195,8 +195,16 @@ test "New master [join $addr {:}] role matches" {
|
||||
}
|
||||
|
||||
test "SENTINEL RESET can resets the master" {
|
||||
assert_equal 1 [S 0 SENTINEL RESET mymaster]
|
||||
assert_equal 0 [llength [S 0 SENTINEL SENTINELS mymaster]]
|
||||
assert_equal 0 [llength [S 0 SENTINEL SLAVES mymaster]]
|
||||
assert_equal 0 [llength [S 0 SENTINEL REPLICAS mymaster]]
|
||||
# After SENTINEL RESET, sometimes the sentinel can sense the master again,
|
||||
# causing the test to fail. Here we give it a few more chances.
|
||||
for {set j 0} {$j < 10} {incr j} {
|
||||
assert_equal 1 [S 0 SENTINEL RESET mymaster]
|
||||
set res1 [llength [S 0 SENTINEL SENTINELS mymaster]]
|
||||
set res2 [llength [S 0 SENTINEL SLAVES mymaster]]
|
||||
set res3 [llength [S 0 SENTINEL REPLICAS mymaster]]
|
||||
if {$res1 eq 0 && $res2 eq 0 && $res3 eq 0} break
|
||||
}
|
||||
assert_equal 0 $res1
|
||||
assert_equal 0 $res2
|
||||
assert_equal 0 $res3
|
||||
}
|
||||
|
@ -62,8 +62,8 @@ proc sanitizer_errors_from_file {filename} {
|
||||
}
|
||||
|
||||
# GCC UBSAN output does not contain 'Sanitizer' but 'runtime error'.
|
||||
if {[string match {*runtime error*} $log] ||
|
||||
[string match {*Sanitizer*} $log]} {
|
||||
if {[string match {*runtime error*} $line] ||
|
||||
[string match {*Sanitizer*} $line]} {
|
||||
return $log
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,15 @@ start_server {tags {"acl external:skip"}} {
|
||||
catch {r ACL SETUSER selector-syntax on (&* &fail)} e
|
||||
assert_match "*ERR Error in ACL SETUSER modifier '(*)*Adding a pattern after the*" $e
|
||||
|
||||
catch {r ACL SETUSER selector-syntax on (+PING (+SELECT (+DEL} e
|
||||
assert_match "*ERR Unmatched parenthesis in acl selector*" $e
|
||||
|
||||
catch {r ACL SETUSER selector-syntax on (+PING (+SELECT (+DEL ) ) ) } e
|
||||
assert_match "*ERR Error in ACL SETUSER modifier*" $e
|
||||
|
||||
catch {r ACL SETUSER selector-syntax on (+PING (+SELECT (+DEL ) } e
|
||||
assert_match "*ERR Error in ACL SETUSER modifier*" $e
|
||||
|
||||
assert_equal "" [r ACL GETUSER selector-syntax]
|
||||
}
|
||||
|
||||
|
@ -615,6 +615,10 @@ start_server {tags {"acl external:skip"}} {
|
||||
# Unnecessary categories are retained for potentional future compatibility
|
||||
r ACL SETUSER adv-test -@all -@dangerous
|
||||
assert_equal "-@all -@dangerous" [dict get [r ACL getuser adv-test] commands]
|
||||
|
||||
# Duplicate categories are compressed, regression test for #12470
|
||||
r ACL SETUSER adv-test -@all +config +config|get -config|set +config
|
||||
assert_equal "-@all +config" [dict get [r ACL getuser adv-test] commands]
|
||||
}
|
||||
|
||||
test "ACL CAT with illegal arguments" {
|
||||
|
@ -70,8 +70,8 @@ start_server {tags {"auth_binary_password external:skip"}} {
|
||||
|
||||
# Configure the replica with masterauth
|
||||
set loglines [count_log_lines 0]
|
||||
$slave slaveof $master_host $master_port
|
||||
$slave config set masterauth "abc"
|
||||
$slave slaveof $master_host $master_port
|
||||
|
||||
# Verify replica is not able to sync with master
|
||||
wait_for_log_messages 0 {"*Unable to AUTH to MASTER*"} $loglines 1000 10
|
||||
|
@ -81,6 +81,23 @@ start_multiple_servers 3 [list overrides $base_conf] {
|
||||
|
||||
set node1_rd [redis_deferring_client 0]
|
||||
|
||||
test "use previous hostip in \"cluster-preferred-endpoint-type unknown-endpoint\" mode" {
|
||||
|
||||
# backup and set cluster-preferred-endpoint-type unknown-endpoint
|
||||
set endpoint_type_before_set [lindex [split [$node1 CONFIG GET cluster-preferred-endpoint-type] " "] 1]
|
||||
$node1 CONFIG SET cluster-preferred-endpoint-type unknown-endpoint
|
||||
|
||||
# when redis-cli not in cluster mode, return MOVE with empty host
|
||||
set slot_for_foo [$node1 CLUSTER KEYSLOT foo]
|
||||
assert_error "*MOVED $slot_for_foo :*" {$node1 set foo bar}
|
||||
|
||||
# when in cluster mode, redirect using previous hostip
|
||||
assert_equal "[exec src/redis-cli -h 127.0.0.1 -p [srv 0 port] -c set foo bar]" {OK}
|
||||
assert_match "[exec src/redis-cli -h 127.0.0.1 -p [srv 0 port] -c get foo]" {bar}
|
||||
|
||||
assert_equal [$node1 CONFIG SET cluster-preferred-endpoint-type "$endpoint_type_before_set"] {OK}
|
||||
}
|
||||
|
||||
test "Sanity test push cmd after resharding" {
|
||||
assert_error {*MOVED*} {$node3 lpush key9184688 v1}
|
||||
|
||||
|
@ -307,6 +307,13 @@ start_server {tags {"scripting"}} {
|
||||
set e
|
||||
} {*against a key*}
|
||||
|
||||
test {EVAL - JSON string encoding a string larger than 2GB} {
|
||||
run_script {
|
||||
local s = string.rep("a", 1024 * 1024 * 1024)
|
||||
return #cjson.encode(s..s..s)
|
||||
} 0
|
||||
} {3221225474} {large-memory} ;# length includes two double quotes at both ends
|
||||
|
||||
test {EVAL - JSON numeric decoding} {
|
||||
# We must return the table as a string because otherwise
|
||||
# Redis converts floats to ints and we get 0 and 1023 instead
|
||||
@ -1112,6 +1119,53 @@ start_server {tags {"scripting"}} {
|
||||
r ping
|
||||
} {PONG}
|
||||
|
||||
test {Timedout scripts and unblocked command} {
|
||||
# make sure a command that's allowed during BUSY doesn't trigger an unblocked command
|
||||
|
||||
# enable AOF to also expose an assertion if the bug would happen
|
||||
r flushall
|
||||
r config set appendonly yes
|
||||
|
||||
# create clients, and set one to block waiting for key 'x'
|
||||
set rd [redis_deferring_client]
|
||||
set rd2 [redis_deferring_client]
|
||||
set r3 [redis_client]
|
||||
$rd2 blpop x 0
|
||||
wait_for_blocked_clients_count 1
|
||||
|
||||
# hack: allow the script to use client list command so that we can control when it aborts
|
||||
r DEBUG set-disable-deny-scripts 1
|
||||
r config set lua-time-limit 10
|
||||
run_script_on_connection $rd {
|
||||
local clients
|
||||
redis.call('lpush',KEYS[1],'y');
|
||||
while true do
|
||||
clients = redis.call('client','list')
|
||||
if string.find(clients, 'abortscript') ~= nil then break end
|
||||
end
|
||||
redis.call('lpush',KEYS[1],'z');
|
||||
return clients
|
||||
} 1 x
|
||||
|
||||
# wait for the script to be busy
|
||||
after 200
|
||||
catch {r ping} e
|
||||
assert_match {BUSY*} $e
|
||||
|
||||
# run cause the script to abort, and run a command that could have processed
|
||||
# unblocked clients (due to a bug)
|
||||
$r3 hello 2 setname abortscript
|
||||
|
||||
# make sure the script completed before the pop was processed
|
||||
assert_equal [$rd2 read] {x z}
|
||||
assert_match {*abortscript*} [$rd read]
|
||||
|
||||
$rd close
|
||||
$rd2 close
|
||||
$r3 close
|
||||
r DEBUG set-disable-deny-scripts 0
|
||||
} {OK} {external:skip needs:debug}
|
||||
|
||||
test {Timedout scripts that modified data can't be killed by SCRIPT KILL} {
|
||||
set rd [redis_deferring_client]
|
||||
r config set lua-time-limit 10
|
||||
|
@ -182,4 +182,33 @@ start_server {tags {"incr"}} {
|
||||
r incrbyfloat foo [expr double(-1)/41]
|
||||
r get foo
|
||||
} {0}
|
||||
|
||||
foreach cmd {"incr" "decr" "incrby" "decrby"} {
|
||||
test "$cmd operation should update encoding from raw to int" {
|
||||
set res {}
|
||||
set expected {1 12}
|
||||
if {[string match {*incr*} $cmd]} {
|
||||
lappend expected 13
|
||||
} else {
|
||||
lappend expected 11
|
||||
}
|
||||
|
||||
r set foo 1
|
||||
assert_encoding "int" foo
|
||||
lappend res [r get foo]
|
||||
|
||||
r append foo 2
|
||||
assert_encoding "raw" foo
|
||||
lappend res [r get foo]
|
||||
|
||||
if {[string match {*by*} $cmd]} {
|
||||
r $cmd foo 1
|
||||
} else {
|
||||
r $cmd foo
|
||||
}
|
||||
assert_encoding "int" foo
|
||||
lappend res [r get foo]
|
||||
assert_equal $res $expected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -656,4 +656,19 @@ if {[string match {*jemalloc*} [s mem_allocator]]} {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {APPEND modifies the encoding from int to raw} {
|
||||
r del foo
|
||||
r set foo 1
|
||||
assert_encoding "int" foo
|
||||
r append foo 2
|
||||
|
||||
set res {}
|
||||
lappend res [r get foo]
|
||||
assert_encoding "raw" foo
|
||||
|
||||
r set bar 12
|
||||
assert_encoding "int" bar
|
||||
lappend res [r get bar]
|
||||
} {12 12}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user