2 * Copyright (c) 2018-2019 Nokia.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "redismodule.h"
23 #include "../../redismodule/include/redismodule.h"
26 #include "exstringsStub.h"
29 /* make sure the response is not NULL or an error.
30 sends the error to the client and exit the current function if its */
31 #define ASSERT_NOERROR(r) \
33 return RedisModule_ReplyWithError(ctx,"ERR reply is NULL"); \
34 } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \
35 RedisModule_ReplyWithCallReply(ctx,r); \
36 RedisModule_FreeCallReply(r); \
37 return REDISMODULE_ERR; \
41 #define OBJ_OP_XX (1<<1) /* OP if key exist */
42 #define OBJ_OP_NX (1<<2) /* OP if key not exist */
43 #define OBJ_OP_IE (1<<4) /* OP if equal old value */
44 #define OBJ_OP_NE (1<<5) /* OP if not equal old value */
46 int getKeyType(RedisModuleCtx *ctx, RedisModuleString *key_str)
48 RedisModuleKey *key = RedisModule_OpenKey(ctx, key_str, REDISMODULE_READ);
49 int type = RedisModule_KeyType(key);
50 RedisModule_CloseKey(key);
54 bool replyContentsEqualString(RedisModuleCallReply *reply, RedisModuleString *expected_value)
56 size_t replylen = 0, expectedlen = 0;
57 const char *expectedval = RedisModule_StringPtrLen(expected_value, &expectedlen);
58 const char *replyval = RedisModule_CallReplyStringPtr(reply, &replylen);
60 expectedlen == replylen &&
61 !strncmp(expectedval, replyval, replylen);
64 typedef struct _SetParams {
65 RedisModuleString **key_val_pairs;
69 typedef struct _PubParams {
70 RedisModuleString **channel_msg_pairs;
74 typedef struct _DelParams {
75 RedisModuleString **keys;
79 void multiPubCommand(RedisModuleCtx *ctx, PubParams* pubParams)
81 RedisModuleCallReply *reply = NULL;
82 for (unsigned int i = 0 ; i < pubParams->length ; i += 2) {
83 reply = RedisModule_Call(ctx, "PUBLISH", "v", pubParams->channel_msg_pairs + i, 2);
84 RedisModule_FreeCallReply(reply);
88 int setStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
89 int argc, const int flag)
91 RedisModuleString *oldvalstr = NULL;
92 RedisModuleCallReply *reply = NULL;
95 return RedisModule_WrongArity(ctx);
99 /*Check if key type is string*/
100 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
102 int type = RedisModule_KeyType(key);
103 RedisModule_CloseKey(key);
105 if (type == REDISMODULE_KEYTYPE_EMPTY) {
106 if (flag == OBJ_OP_IE){
107 RedisModule_ReplyWithNull(ctx);
108 return REDISMODULE_OK;
110 } else if (type != REDISMODULE_KEYTYPE_STRING) {
111 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
115 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
116 ASSERT_NOERROR(reply)
117 size_t curlen=0, oldvallen=0;
118 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
119 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
120 if (((flag == OBJ_OP_IE) &&
121 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
123 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
124 !strncmp(oldval, curval, curlen))) {
125 RedisModule_FreeCallReply(reply);
126 return RedisModule_ReplyWithNull(ctx);
128 RedisModule_FreeCallReply(reply);
130 /* Prepare the arguments for the command. */
131 int i, j=0, cmdargc=argc-2;
132 RedisModuleString *cmdargv[cmdargc];
133 for (i = 1; i < argc; i++) {
136 cmdargv[j++] = argv[i];
139 /* Call the command and pass back the reply. */
140 reply = RedisModule_Call(ctx, "SET", "v!", cmdargv, cmdargc);
141 ASSERT_NOERROR(reply)
142 RedisModule_ReplyWithCallReply(ctx, reply);
144 RedisModule_FreeCallReply(reply);
145 return REDISMODULE_OK;
148 int SetIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
150 return setStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
153 int SetNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
155 return setStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
158 int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
159 int argc, const int flag)
161 RedisModuleString *oldvalstr = NULL;
162 RedisModuleCallReply *reply = NULL;
167 return RedisModule_WrongArity(ctx);
169 /*Check if key type is string*/
170 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
172 int type = RedisModule_KeyType(key);
173 RedisModule_CloseKey(key);
175 if (type == REDISMODULE_KEYTYPE_EMPTY) {
176 return RedisModule_ReplyWithLongLong(ctx, 0);
177 } else if (type != REDISMODULE_KEYTYPE_STRING) {
178 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
182 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
183 ASSERT_NOERROR(reply)
184 size_t curlen = 0, oldvallen = 0;
185 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
186 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
187 if (((flag == OBJ_OP_IE) &&
188 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
190 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
191 !strncmp(oldval, curval, curlen))) {
192 RedisModule_FreeCallReply(reply);
193 return RedisModule_ReplyWithLongLong(ctx, 0);
195 RedisModule_FreeCallReply(reply);
197 /* Prepare the arguments for the command. */
199 RedisModuleString *cmdargv[1];
200 cmdargv[0] = argv[1];
202 /* Call the command and pass back the reply. */
203 reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, cmdargc);
204 ASSERT_NOERROR(reply)
205 RedisModule_ReplyWithCallReply(ctx, reply);
207 RedisModule_FreeCallReply(reply);
208 return REDISMODULE_OK;
211 int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
213 return delStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
216 int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
218 return delStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
221 int NGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
223 RedisModuleCallReply *reply = NULL;
226 return RedisModule_WrongArity(ctx);
228 /* Call the command to get keys with pattern. */
229 reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
230 ASSERT_NOERROR(reply)
232 /* Prepare the arguments for the command. */
233 size_t items = RedisModule_CallReplyLength(reply);
235 //RedisModule_ReplyWithArray(ctx, items);
236 RedisModule_ReplyWithCallReply(ctx, reply);
237 RedisModule_FreeCallReply(reply);
240 RedisModuleString *cmdargv[items];
242 for (j = 0; j < items; j++) {
243 RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
246 /*Assume all keys via SDL is string type for sake of saving time*/
248 /*Check if key type is string*/
249 RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
252 int type = RedisModule_KeyType(key);
253 RedisModule_CloseKey(key);
254 if (type == REDISMODULE_KEYTYPE_STRING) {
258 RedisModule_CloseKey(key);
262 RedisModule_FreeCallReply(reply);
264 reply = RedisModule_Call(ctx, "MGET", "v", cmdargv, i);
265 ASSERT_NOERROR(reply)
266 items = RedisModule_CallReplyLength(reply);
267 RedisModule_ReplyWithArray(ctx, i*2);
268 for (j = 0; (j<items && j<i); j++) {
269 RedisModule_ReplyWithString(ctx, cmdargv[j]);
270 RedisModule_ReplyWithString(ctx, RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j)));
273 RedisModule_FreeCallReply(reply);
276 return REDISMODULE_OK;
279 int NDel_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
281 RedisModuleCallReply *reply = NULL;
284 return RedisModule_WrongArity(ctx);
286 /* Call the command to get keys with pattern. */
287 reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
288 ASSERT_NOERROR(reply)
290 /* Prepare the arguments for the command. */
291 size_t items = RedisModule_CallReplyLength(reply);
293 RedisModule_ReplyWithLongLong(ctx, 0);
294 RedisModule_FreeCallReply(reply);
297 RedisModuleString *cmdargv[items];
299 for (j = 0; j < items; j++) {
300 RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
303 /*Assume all keys via SDL is string type for sake of saving time*/
305 //Check if key type is string
306 RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
309 int type = RedisModule_KeyType(key);
310 RedisModule_CloseKey(key);
311 if (type == REDISMODULE_KEYTYPE_STRING) {
315 RedisModule_CloseKey(key);
319 RedisModule_FreeCallReply(reply);
321 reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, i);
322 ASSERT_NOERROR(reply)
323 RedisModule_ReplyWithCallReply(ctx, reply);
324 RedisModule_FreeCallReply(reply);
328 return REDISMODULE_OK;
331 int setPubStringCommon(RedisModuleCtx *ctx, SetParams* setParamsPtr, PubParams* pubParamsPtr)
333 RedisModuleCallReply *setReply;
334 setReply = RedisModule_Call(ctx, "MSET", "v!", setParamsPtr->key_val_pairs, setParamsPtr->length);
335 ASSERT_NOERROR(setReply)
336 int replytype = RedisModule_CallReplyType(setReply);
337 if (replytype == REDISMODULE_REPLY_NULL) {
338 RedisModule_ReplyWithNull(ctx);
340 multiPubCommand(ctx, pubParamsPtr);
341 RedisModule_ReplyWithCallReply(ctx, setReply);
343 RedisModule_FreeCallReply(setReply);
344 return REDISMODULE_OK;
347 int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
349 if (argc < 5 || (argc % 2) == 0)
350 return RedisModule_WrongArity(ctx);
352 SetParams setParams = {
353 .key_val_pairs = argv + 1,
356 PubParams pubParams = {
357 .channel_msg_pairs = argv + argc - 2,
361 return setPubStringCommon(ctx, &setParams, &pubParams);
364 int SetMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
366 if (argc < 7 || (argc % 2) == 0)
367 return RedisModule_WrongArity(ctx);
369 long long setPairsCount, pubPairsCount;
370 RedisModule_StringToLongLong(argv[1], &setPairsCount);
371 RedisModule_StringToLongLong(argv[2], &pubPairsCount);
372 if (setPairsCount < 1 || pubPairsCount < 1)
373 return RedisModule_ReplyWithError(ctx, "ERR SET_PAIR_COUNT and PUB_PAIR_COUNT must be greater than zero");
375 long long setLen, pubLen;
376 setLen = 2*setPairsCount;
377 pubLen = 2*pubPairsCount;
378 if (setLen + pubLen + 3 != argc)
379 return RedisModule_ReplyWithError(ctx, "ERR SET_PAIR_COUNT or PUB_PAIR_COUNT do not match the total pair count");
381 SetParams setParams = {
382 .key_val_pairs = argv + 3,
385 PubParams pubParams = {
386 .channel_msg_pairs = argv + 3 + setParams.length,
390 return setPubStringCommon(ctx, &setParams, &pubParams);
393 int setIENEPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
395 if (argc < 6 || (argc % 2) != 0)
396 return RedisModule_WrongArity(ctx);
398 SetParams setParams = {
399 .key_val_pairs = argv + 1,
402 PubParams pubParams = {
403 .channel_msg_pairs = argv + 4,
406 RedisModuleString *key = setParams.key_val_pairs[0];
407 RedisModuleString *oldvalstr = argv[3];
409 int type = getKeyType(ctx, key);
410 if (flag == OBJ_OP_IE && type == REDISMODULE_KEYTYPE_EMPTY) {
411 return RedisModule_ReplyWithNull(ctx);
412 } else if (type != REDISMODULE_KEYTYPE_STRING && type != REDISMODULE_KEYTYPE_EMPTY) {
413 return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
416 RedisModuleCallReply *reply = RedisModule_Call(ctx, "GET", "s", key);
417 ASSERT_NOERROR(reply)
418 bool is_equal = replyContentsEqualString(reply, oldvalstr);
419 RedisModule_FreeCallReply(reply);
420 if ((flag == OBJ_OP_IE && !is_equal) ||
421 (flag == OBJ_OP_NE && is_equal)) {
422 return RedisModule_ReplyWithNull(ctx);
425 return setPubStringCommon(ctx, &setParams, &pubParams);
428 int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
430 return setIENEPubStringCommon(ctx, argv, argc, OBJ_OP_NE);
433 int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
435 return setIENEPubStringCommon(ctx, argv, argc, OBJ_OP_IE);
438 int setXXNXPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
440 if (argc < 5 || (argc % 2) == 0)
441 return RedisModule_WrongArity(ctx);
443 SetParams setParams = {
444 .key_val_pairs = argv + 1,
447 PubParams pubParams = {
448 .channel_msg_pairs = argv + 3,
451 RedisModuleString *key = setParams.key_val_pairs[0];
453 int type = getKeyType(ctx, key);
454 if ((flag == OBJ_OP_XX && type == REDISMODULE_KEYTYPE_EMPTY) ||
455 (flag == OBJ_OP_NX && type == REDISMODULE_KEYTYPE_STRING)) {
456 return RedisModule_ReplyWithNull(ctx);
457 } else if (type != REDISMODULE_KEYTYPE_STRING && type != REDISMODULE_KEYTYPE_EMPTY) {
458 RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
459 return REDISMODULE_OK;
462 return setPubStringCommon(ctx, &setParams, &pubParams);
465 int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
467 return setXXNXPubStringCommon(ctx, argv, argc, OBJ_OP_NX);
470 int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
472 return setXXNXPubStringCommon(ctx, argv, argc, OBJ_OP_XX);
475 int delPubStringCommon(RedisModuleCtx *ctx, DelParams *delParamsPtr, PubParams *pubParamsPtr)
477 RedisModuleCallReply *reply = RedisModule_Call(ctx, "UNLINK", "v!", delParamsPtr->keys, delParamsPtr->length);
478 ASSERT_NOERROR(reply)
479 int replytype = RedisModule_CallReplyType(reply);
480 if (replytype == REDISMODULE_REPLY_NULL) {
481 RedisModule_ReplyWithNull(ctx);
482 } else if (RedisModule_CallReplyInteger(reply) == 0) {
483 RedisModule_ReplyWithCallReply(ctx, reply);
485 RedisModule_ReplyWithCallReply(ctx, reply);
486 multiPubCommand(ctx, pubParamsPtr);
488 RedisModule_FreeCallReply(reply);
489 return REDISMODULE_OK;
492 int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
495 return RedisModule_WrongArity(ctx);
497 DelParams delParams = {
501 PubParams pubParams = {
502 .channel_msg_pairs = argv + 1 + delParams.length,
506 return delPubStringCommon(ctx, &delParams, &pubParams);
509 int DelMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
512 return RedisModule_WrongArity(ctx);
514 long long delCount, pubPairsCount;
515 RedisModule_StringToLongLong(argv[1], &delCount);
516 RedisModule_StringToLongLong(argv[2], &pubPairsCount);
517 if (delCount < 1 || pubPairsCount < 1)
518 return RedisModule_ReplyWithError(ctx, "ERR DEL_COUNT and PUB_PAIR_COUNT must be greater than zero");
520 long long delLen, pubLen;
522 pubLen = 2*pubPairsCount;
523 if (delLen + pubLen + 3 != argc)
524 return RedisModule_ReplyWithError(ctx, "ERR DEL_COUNT or PUB_PAIR_COUNT do not match the total pair count");
526 DelParams delParams = {
530 PubParams pubParams = {
531 .channel_msg_pairs = argv + 3 + delParams.length,
535 return delPubStringCommon(ctx, &delParams, &pubParams);
538 int delIENEPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
540 if (argc < 5 || (argc % 2) == 0)
541 return RedisModule_WrongArity(ctx);
543 DelParams delParams = {
547 PubParams pubParams = {
548 .channel_msg_pairs = argv + 3,
551 RedisModuleString *key = argv[1];
552 RedisModuleString *oldvalstr = argv[2];
554 int type = getKeyType(ctx, key);
555 if (type == REDISMODULE_KEYTYPE_EMPTY) {
556 return RedisModule_ReplyWithLongLong(ctx, 0);
557 } else if (type != REDISMODULE_KEYTYPE_STRING) {
558 return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
561 RedisModuleCallReply *reply = RedisModule_Call(ctx, "GET", "s", key);
562 ASSERT_NOERROR(reply)
563 bool is_equal = replyContentsEqualString(reply, oldvalstr);
564 RedisModule_FreeCallReply(reply);
565 if ((flag == OBJ_OP_IE && !is_equal) ||
566 (flag == OBJ_OP_NE && is_equal)) {
567 return RedisModule_ReplyWithLongLong(ctx, 0);
570 return delPubStringCommon(ctx, &delParams, &pubParams);
573 int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
575 return delIENEPubStringCommon(ctx, argv, argc, OBJ_OP_IE);
578 int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
580 return delIENEPubStringCommon(ctx, argv, argc, OBJ_OP_NE);
583 /* This function must be present on each Redis module. It is used in order to
584 * register the commands into the Redis server. */
585 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
586 REDISMODULE_NOT_USED(argv);
587 REDISMODULE_NOT_USED(argc);
589 if (RedisModule_Init(ctx,"exstrings",1,REDISMODULE_APIVER_1)
590 == REDISMODULE_ERR) return REDISMODULE_ERR;
592 if (RedisModule_CreateCommand(ctx,"setie",
593 SetIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
594 return REDISMODULE_ERR;
596 if (RedisModule_CreateCommand(ctx,"setne",
597 SetNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
598 return REDISMODULE_ERR;
600 if (RedisModule_CreateCommand(ctx,"delie",
601 DelIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
602 return REDISMODULE_ERR;
604 if (RedisModule_CreateCommand(ctx,"delne",
605 DelNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
606 return REDISMODULE_ERR;
608 if (RedisModule_CreateCommand(ctx,"nget",
609 NGet_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
610 return REDISMODULE_ERR;
612 if (RedisModule_CreateCommand(ctx,"ndel",
613 NDel_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
614 return REDISMODULE_ERR;
616 if (RedisModule_CreateCommand(ctx,"msetpub",
617 SetPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
618 return REDISMODULE_ERR;
620 if (RedisModule_CreateCommand(ctx,"msetmpub",
621 SetMPub_RedisCommand,"write deny-oom pubsub",1,1,1) == REDISMODULE_ERR)
622 return REDISMODULE_ERR;
624 if (RedisModule_CreateCommand(ctx,"setiepub",
625 SetIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
626 return REDISMODULE_ERR;
628 if (RedisModule_CreateCommand(ctx,"setnepub",
629 SetNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
630 return REDISMODULE_ERR;
632 if (RedisModule_CreateCommand(ctx,"setxxpub",
633 SetXXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
634 return REDISMODULE_ERR;
636 if (RedisModule_CreateCommand(ctx,"setnxpub",
637 SetNXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
638 return REDISMODULE_ERR;
640 if (RedisModule_CreateCommand(ctx,"delpub",
641 DelPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
642 return REDISMODULE_ERR;
644 if (RedisModule_CreateCommand(ctx,"delmpub",
645 DelMPub_RedisCommand,"write deny-oom pubsub",1,1,1) == REDISMODULE_ERR)
646 return REDISMODULE_ERR;
648 if (RedisModule_CreateCommand(ctx,"deliepub",
649 DelIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
650 return REDISMODULE_ERR;
652 if (RedisModule_CreateCommand(ctx,"delnepub",
653 DelNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
654 return REDISMODULE_ERR;
656 return REDISMODULE_OK;