2 * Copyright (c) 2018-2020 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.
18 * This source code is part of the near-RT RIC (RAN Intelligent Controller)
19 * platform project (RICP).
22 #include "redismodule.h"
28 #include "exstringsStub.h"
29 #include "commonStub.h"
33 /* make sure the response is not NULL or an error.
34 sends the error to the client and exit the current function if its */
35 #define ASSERT_NOERROR(r) \
37 return RedisModule_ReplyWithError(ctx,"ERR reply is NULL"); \
38 } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \
39 return RedisModule_ReplyWithCallReply(ctx,r); \
43 #define OBJ_OP_XX (1<<1) /* OP if key exist */
44 #define OBJ_OP_NX (1<<2) /* OP if key not exist */
45 #define OBJ_OP_IE (1<<4) /* OP if equal old value */
46 #define OBJ_OP_NE (1<<5) /* OP if not equal old value */
50 #define MATCH_STR "MATCH"
51 #define COUNT_STR "COUNT"
54 RedisModuleString *def_count_str = NULL, *match_str = NULL, *count_str = NULL, *zero_str = NULL;
56 void InitStaticVariable()
58 if (def_count_str == NULL)
59 def_count_str = RedisModule_CreateStringFromLongLong(NULL, DEF_COUNT);
60 if (match_str == NULL)
61 match_str = RedisModule_CreateString(NULL, MATCH_STR, sizeof(MATCH_STR));
62 if (count_str == NULL)
63 count_str = RedisModule_CreateString(NULL, COUNT_STR, sizeof(COUNT_STR));
65 zero_str = RedisModule_CreateStringFromLongLong(NULL, ZERO);
70 int getKeyType(RedisModuleCtx *ctx, RedisModuleString *key_str)
72 RedisModuleKey *key = RedisModule_OpenKey(ctx, key_str, REDISMODULE_READ);
73 int type = RedisModule_KeyType(key);
74 RedisModule_CloseKey(key);
78 bool replyContentsEqualString(RedisModuleCallReply *reply, RedisModuleString *expected_value)
80 size_t replylen = 0, expectedlen = 0;
81 const char *expectedval = RedisModule_StringPtrLen(expected_value, &expectedlen);
82 const char *replyval = RedisModule_CallReplyStringPtr(reply, &replylen);
84 expectedlen == replylen &&
85 !strncmp(expectedval, replyval, replylen);
88 typedef struct _SetParams {
89 RedisModuleString **key_val_pairs;
93 typedef struct _PubParams {
94 RedisModuleString **channel_msg_pairs;
98 typedef struct _DelParams {
99 RedisModuleString **keys;
103 void multiPubCommand(RedisModuleCtx *ctx, PubParams* pubParams)
105 RedisModuleCallReply *reply = NULL;
106 for (unsigned int i = 0 ; i < pubParams->length ; i += 2) {
107 reply = RedisModule_Call(ctx, "PUBLISH", "v", pubParams->channel_msg_pairs + i, 2);
108 RedisModule_FreeCallReply(reply);
112 int setStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
113 int argc, const int flag)
115 RedisModuleString *oldvalstr = NULL;
116 RedisModuleCallReply *reply = NULL;
119 return RedisModule_WrongArity(ctx);
123 /*Check if key type is string*/
124 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
126 int type = RedisModule_KeyType(key);
127 RedisModule_CloseKey(key);
129 if (type == REDISMODULE_KEYTYPE_EMPTY) {
130 if (flag == OBJ_OP_IE){
131 RedisModule_ReplyWithNull(ctx);
132 return REDISMODULE_OK;
134 } else if (type != REDISMODULE_KEYTYPE_STRING) {
135 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
139 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
140 ASSERT_NOERROR(reply)
141 size_t curlen=0, oldvallen=0;
142 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
143 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
144 if (((flag == OBJ_OP_IE) &&
145 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
147 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
148 !strncmp(oldval, curval, curlen))) {
149 RedisModule_FreeCallReply(reply);
150 return RedisModule_ReplyWithNull(ctx);
152 RedisModule_FreeCallReply(reply);
154 /* Prepare the arguments for the command. */
155 int i, j=0, cmdargc=argc-2;
156 RedisModuleString *cmdargv[cmdargc];
157 for (i = 1; i < argc; i++) {
160 cmdargv[j++] = argv[i];
163 /* Call the command and pass back the reply. */
164 reply = RedisModule_Call(ctx, "SET", "v!", cmdargv, cmdargc);
165 ASSERT_NOERROR(reply)
166 RedisModule_ReplyWithCallReply(ctx, reply);
168 RedisModule_FreeCallReply(reply);
169 return REDISMODULE_OK;
172 int SetIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
174 RedisModule_AutoMemory(ctx);
175 return setStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
178 int SetNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
180 RedisModule_AutoMemory(ctx);
181 return setStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
184 int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
185 int argc, const int flag)
187 RedisModuleString *oldvalstr = NULL;
188 RedisModuleCallReply *reply = NULL;
193 return RedisModule_WrongArity(ctx);
195 /*Check if key type is string*/
196 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
198 int type = RedisModule_KeyType(key);
199 RedisModule_CloseKey(key);
201 if (type == REDISMODULE_KEYTYPE_EMPTY) {
202 return RedisModule_ReplyWithLongLong(ctx, 0);
203 } else if (type != REDISMODULE_KEYTYPE_STRING) {
204 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
208 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
209 ASSERT_NOERROR(reply)
210 size_t curlen = 0, oldvallen = 0;
211 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
212 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
213 if (((flag == OBJ_OP_IE) &&
214 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
216 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
217 !strncmp(oldval, curval, curlen))) {
218 RedisModule_FreeCallReply(reply);
219 return RedisModule_ReplyWithLongLong(ctx, 0);
221 RedisModule_FreeCallReply(reply);
223 /* Prepare the arguments for the command. */
225 RedisModuleString *cmdargv[1];
226 cmdargv[0] = argv[1];
228 /* Call the command and pass back the reply. */
229 reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, cmdargc);
230 ASSERT_NOERROR(reply)
231 RedisModule_ReplyWithCallReply(ctx, reply);
233 RedisModule_FreeCallReply(reply);
234 return REDISMODULE_OK;
237 int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
239 RedisModule_AutoMemory(ctx);
240 return delStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
243 int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
245 RedisModule_AutoMemory(ctx);
246 return delStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
248 int setPubStringCommon(RedisModuleCtx *ctx, SetParams* setParamsPtr, PubParams* pubParamsPtr)
250 RedisModuleCallReply *setReply;
251 setReply = RedisModule_Call(ctx, "MSET", "v!", setParamsPtr->key_val_pairs, setParamsPtr->length);
252 ASSERT_NOERROR(setReply)
253 multiPubCommand(ctx, pubParamsPtr);
254 RedisModule_ReplyWithCallReply(ctx, setReply);
255 RedisModule_FreeCallReply(setReply);
256 return REDISMODULE_OK;
259 int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
261 if (argc < 5 || (argc % 2) == 0)
262 return RedisModule_WrongArity(ctx);
264 RedisModule_AutoMemory(ctx);
265 SetParams setParams = {
266 .key_val_pairs = argv + 1,
269 PubParams pubParams = {
270 .channel_msg_pairs = argv + 1 + setParams.length,
274 return setPubStringCommon(ctx, &setParams, &pubParams);
277 int SetMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
279 if (argc < 7 || (argc % 2) == 0)
280 return RedisModule_WrongArity(ctx);
282 RedisModule_AutoMemory(ctx);
283 long long setPairsCount, pubPairsCount;
284 RedisModule_StringToLongLong(argv[1], &setPairsCount);
285 RedisModule_StringToLongLong(argv[2], &pubPairsCount);
286 if (setPairsCount < 1 || pubPairsCount < 1)
287 return RedisModule_ReplyWithError(ctx, "ERR SET_PAIR_COUNT and PUB_PAIR_COUNT must be greater than zero");
289 long long setLen, pubLen;
290 setLen = 2*setPairsCount;
291 pubLen = 2*pubPairsCount;
293 if (setLen + pubLen + 3 != argc)
294 return RedisModule_ReplyWithError(ctx, "ERR SET_PAIR_COUNT or PUB_PAIR_COUNT do not match the total pair count");
296 SetParams setParams = {
297 .key_val_pairs = argv + 3,
300 PubParams pubParams = {
301 .channel_msg_pairs = argv + 3 + setParams.length,
305 return setPubStringCommon(ctx, &setParams, &pubParams);
308 int setIENEPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
310 SetParams setParams = {
311 .key_val_pairs = argv + 1,
314 PubParams pubParams = {
315 .channel_msg_pairs = argv + 4,
318 RedisModuleString *key = setParams.key_val_pairs[0];
319 RedisModuleString *oldvalstr = argv[3];
321 int type = getKeyType(ctx, key);
322 if (flag == OBJ_OP_IE && type == REDISMODULE_KEYTYPE_EMPTY) {
323 return RedisModule_ReplyWithNull(ctx);
324 } else if (type != REDISMODULE_KEYTYPE_STRING && type != REDISMODULE_KEYTYPE_EMPTY) {
325 return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
328 RedisModuleCallReply *reply = RedisModule_Call(ctx, "GET", "s", key);
329 ASSERT_NOERROR(reply)
330 bool is_equal = replyContentsEqualString(reply, oldvalstr);
331 RedisModule_FreeCallReply(reply);
332 if ((flag == OBJ_OP_IE && !is_equal) ||
333 (flag == OBJ_OP_NE && is_equal)) {
334 return RedisModule_ReplyWithNull(ctx);
337 return setPubStringCommon(ctx, &setParams, &pubParams);
340 int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
343 return RedisModule_WrongArity(ctx);
345 RedisModule_AutoMemory(ctx);
346 return setIENEPubStringCommon(ctx, argv, argc, OBJ_OP_IE);
349 int SetIEMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
351 if (argc < 6 || (argc % 2) != 0)
352 return RedisModule_WrongArity(ctx);
354 RedisModule_AutoMemory(ctx);
355 return setIENEPubStringCommon(ctx, argv, argc, OBJ_OP_IE);
358 int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
361 return RedisModule_WrongArity(ctx);
363 RedisModule_AutoMemory(ctx);
364 return setIENEPubStringCommon(ctx, argv, argc, OBJ_OP_NE);
367 int setXXNXPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
369 SetParams setParams = {
370 .key_val_pairs = argv + 1,
373 PubParams pubParams = {
374 .channel_msg_pairs = argv + 3,
377 RedisModuleString *key = setParams.key_val_pairs[0];
379 int type = getKeyType(ctx, key);
380 if ((flag == OBJ_OP_XX && type == REDISMODULE_KEYTYPE_EMPTY) ||
381 (flag == OBJ_OP_NX && type == REDISMODULE_KEYTYPE_STRING)) {
382 return RedisModule_ReplyWithNull(ctx);
383 } else if (type != REDISMODULE_KEYTYPE_STRING && type != REDISMODULE_KEYTYPE_EMPTY) {
384 RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
385 return REDISMODULE_OK;
388 return setPubStringCommon(ctx, &setParams, &pubParams);
391 int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
394 return RedisModule_WrongArity(ctx);
396 RedisModule_AutoMemory(ctx);
397 return setXXNXPubStringCommon(ctx, argv, argc, OBJ_OP_NX);
400 int SetNXMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
402 if (argc < 5 || (argc % 2) == 0)
403 return RedisModule_WrongArity(ctx);
405 RedisModule_AutoMemory(ctx);
406 return setXXNXPubStringCommon(ctx, argv, argc, OBJ_OP_NX);
409 int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
412 return RedisModule_WrongArity(ctx);
414 RedisModule_AutoMemory(ctx);
415 return setXXNXPubStringCommon(ctx, argv, argc, OBJ_OP_XX);
418 int delPubStringCommon(RedisModuleCtx *ctx, DelParams *delParamsPtr, PubParams *pubParamsPtr)
420 RedisModuleCallReply *reply = RedisModule_Call(ctx, "UNLINK", "v!", delParamsPtr->keys, delParamsPtr->length);
421 ASSERT_NOERROR(reply)
422 int replytype = RedisModule_CallReplyType(reply);
423 if (replytype == REDISMODULE_REPLY_NULL) {
424 RedisModule_ReplyWithNull(ctx);
425 } else if (RedisModule_CallReplyInteger(reply) == 0) {
426 RedisModule_ReplyWithCallReply(ctx, reply);
428 RedisModule_ReplyWithCallReply(ctx, reply);
429 multiPubCommand(ctx, pubParamsPtr);
431 RedisModule_FreeCallReply(reply);
432 return REDISMODULE_OK;
435 int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
438 return RedisModule_WrongArity(ctx);
440 RedisModule_AutoMemory(ctx);
441 DelParams delParams = {
445 PubParams pubParams = {
446 .channel_msg_pairs = argv + 1 + delParams.length,
450 return delPubStringCommon(ctx, &delParams, &pubParams);
453 int DelMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
456 return RedisModule_WrongArity(ctx);
458 RedisModule_AutoMemory(ctx);
459 long long delCount, pubPairsCount;
460 RedisModule_StringToLongLong(argv[1], &delCount);
461 RedisModule_StringToLongLong(argv[2], &pubPairsCount);
462 if (delCount < 1 || pubPairsCount < 1)
463 return RedisModule_ReplyWithError(ctx, "ERR DEL_COUNT and PUB_PAIR_COUNT must be greater than zero");
465 long long delLen, pubLen;
467 pubLen = 2*pubPairsCount;
468 if (delLen + pubLen + 3 != argc)
469 return RedisModule_ReplyWithError(ctx, "ERR DEL_COUNT or PUB_PAIR_COUNT do not match the total pair count");
471 DelParams delParams = {
475 PubParams pubParams = {
476 .channel_msg_pairs = argv + 3 + delParams.length,
480 return delPubStringCommon(ctx, &delParams, &pubParams);
483 int delIENEPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
485 DelParams delParams = {
489 PubParams pubParams = {
490 .channel_msg_pairs = argv + 3,
493 RedisModuleString *key = argv[1];
494 RedisModuleString *oldvalstr = argv[2];
496 int type = getKeyType(ctx, key);
497 if (type == REDISMODULE_KEYTYPE_EMPTY) {
498 return RedisModule_ReplyWithLongLong(ctx, 0);
499 } else if (type != REDISMODULE_KEYTYPE_STRING) {
500 return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
503 RedisModuleCallReply *reply = RedisModule_Call(ctx, "GET", "s", key);
504 ASSERT_NOERROR(reply)
505 bool is_equal = replyContentsEqualString(reply, oldvalstr);
506 RedisModule_FreeCallReply(reply);
507 if ((flag == OBJ_OP_IE && !is_equal) ||
508 (flag == OBJ_OP_NE && is_equal)) {
509 return RedisModule_ReplyWithLongLong(ctx, 0);
512 return delPubStringCommon(ctx, &delParams, &pubParams);
515 int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
518 return RedisModule_WrongArity(ctx);
520 RedisModule_AutoMemory(ctx);
521 return delIENEPubStringCommon(ctx, argv, argc, OBJ_OP_IE);
524 int DelIEMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
526 if (argc < 5 || (argc % 2) == 0)
527 return RedisModule_WrongArity(ctx);
529 RedisModule_AutoMemory(ctx);
530 return delIENEPubStringCommon(ctx, argv, argc, OBJ_OP_IE);
533 int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
536 return RedisModule_WrongArity(ctx);
538 RedisModule_AutoMemory(ctx);
539 return delIENEPubStringCommon(ctx, argv, argc, OBJ_OP_NE);
542 /* This function must be present on each Redis module. It is used in order to
543 * register the commands into the Redis server. */
544 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
545 REDISMODULE_NOT_USED(argv);
546 REDISMODULE_NOT_USED(argc);
548 if (RedisModule_Init(ctx,"exstrings",1,REDISMODULE_APIVER_1)
549 == REDISMODULE_ERR) return REDISMODULE_ERR;
551 if (RedisModule_CreateCommand(ctx,"setie",
552 SetIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
553 return REDISMODULE_ERR;
555 if (RedisModule_CreateCommand(ctx,"setne",
556 SetNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
557 return REDISMODULE_ERR;
559 if (RedisModule_CreateCommand(ctx,"delie",
560 DelIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
561 return REDISMODULE_ERR;
563 if (RedisModule_CreateCommand(ctx,"delne",
564 DelNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
565 return REDISMODULE_ERR;
567 if (RedisModule_CreateCommand(ctx,"msetpub",
568 SetPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
569 return REDISMODULE_ERR;
571 if (RedisModule_CreateCommand(ctx,"msetmpub",
572 SetMPub_RedisCommand,"write deny-oom pubsub",1,1,1) == REDISMODULE_ERR)
573 return REDISMODULE_ERR;
575 if (RedisModule_CreateCommand(ctx,"setiepub",
576 SetIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
577 return REDISMODULE_ERR;
579 if (RedisModule_CreateCommand(ctx,"setiempub",
580 SetIEMPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
581 return REDISMODULE_ERR;
583 if (RedisModule_CreateCommand(ctx,"setnepub",
584 SetNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
585 return REDISMODULE_ERR;
587 if (RedisModule_CreateCommand(ctx,"setxxpub",
588 SetXXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
589 return REDISMODULE_ERR;
591 if (RedisModule_CreateCommand(ctx,"setnxpub",
592 SetNXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
593 return REDISMODULE_ERR;
595 if (RedisModule_CreateCommand(ctx,"setnxmpub",
596 SetNXMPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
597 return REDISMODULE_ERR;
599 if (RedisModule_CreateCommand(ctx,"delpub",
600 DelPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
601 return REDISMODULE_ERR;
603 if (RedisModule_CreateCommand(ctx,"delmpub",
604 DelMPub_RedisCommand,"write deny-oom pubsub",1,1,1) == REDISMODULE_ERR)
605 return REDISMODULE_ERR;
607 if (RedisModule_CreateCommand(ctx,"deliepub",
608 DelIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
609 return REDISMODULE_ERR;
611 if (RedisModule_CreateCommand(ctx,"deliempub",
612 DelIEMPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
613 return REDISMODULE_ERR;
615 if (RedisModule_CreateCommand(ctx,"delnepub",
616 DelNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
617 return REDISMODULE_ERR;
619 return REDISMODULE_OK;