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 void multiPubCommand(RedisModuleCtx *ctx, PubParams* pubParams)
76 RedisModuleCallReply *reply = NULL;
77 for (unsigned int i = 0 ; i < pubParams->length ; i += 2) {
78 reply = RedisModule_Call(ctx, "PUBLISH", "v", pubParams->channel_msg_pairs + i, 2);
79 RedisModule_FreeCallReply(reply);
83 int setStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
84 int argc, const int flag)
86 RedisModuleString *oldvalstr = NULL;
87 RedisModuleCallReply *reply = NULL;
90 return RedisModule_WrongArity(ctx);
94 /*Check if key type is string*/
95 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
97 int type = RedisModule_KeyType(key);
98 RedisModule_CloseKey(key);
100 if (type == REDISMODULE_KEYTYPE_EMPTY) {
101 if (flag == OBJ_OP_IE){
102 RedisModule_ReplyWithNull(ctx);
103 return REDISMODULE_OK;
105 } else if (type != REDISMODULE_KEYTYPE_STRING) {
106 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
110 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
111 ASSERT_NOERROR(reply)
112 size_t curlen=0, oldvallen=0;
113 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
114 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
115 if (((flag == OBJ_OP_IE) &&
116 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
118 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
119 !strncmp(oldval, curval, curlen))) {
120 RedisModule_FreeCallReply(reply);
121 return RedisModule_ReplyWithNull(ctx);
123 RedisModule_FreeCallReply(reply);
125 /* Prepare the arguments for the command. */
126 int i, j=0, cmdargc=argc-2;
127 RedisModuleString *cmdargv[cmdargc];
128 for (i = 1; i < argc; i++) {
131 cmdargv[j++] = argv[i];
134 /* Call the command and pass back the reply. */
135 reply = RedisModule_Call(ctx, "SET", "v!", cmdargv, cmdargc);
136 ASSERT_NOERROR(reply)
137 RedisModule_ReplyWithCallReply(ctx, reply);
139 RedisModule_FreeCallReply(reply);
140 return REDISMODULE_OK;
143 int SetIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
145 return setStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
148 int SetNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
150 return setStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
153 int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
154 int argc, const int flag)
156 RedisModuleString *oldvalstr = NULL;
157 RedisModuleCallReply *reply = NULL;
162 return RedisModule_WrongArity(ctx);
164 /*Check if key type is string*/
165 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
167 int type = RedisModule_KeyType(key);
168 RedisModule_CloseKey(key);
170 if (type == REDISMODULE_KEYTYPE_EMPTY) {
171 return RedisModule_ReplyWithLongLong(ctx, 0);
172 } else if (type != REDISMODULE_KEYTYPE_STRING) {
173 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
177 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
178 ASSERT_NOERROR(reply)
179 size_t curlen = 0, oldvallen = 0;
180 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
181 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
182 if (((flag == OBJ_OP_IE) &&
183 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
185 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
186 !strncmp(oldval, curval, curlen))) {
187 RedisModule_FreeCallReply(reply);
188 return RedisModule_ReplyWithLongLong(ctx, 0);
190 RedisModule_FreeCallReply(reply);
192 /* Prepare the arguments for the command. */
194 RedisModuleString *cmdargv[1];
195 cmdargv[0] = argv[1];
197 /* Call the command and pass back the reply. */
198 reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, cmdargc);
199 ASSERT_NOERROR(reply)
200 RedisModule_ReplyWithCallReply(ctx, reply);
202 RedisModule_FreeCallReply(reply);
203 return REDISMODULE_OK;
206 int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
208 return delStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
211 int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
213 return delStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
216 int NGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
218 RedisModuleCallReply *reply = NULL;
221 return RedisModule_WrongArity(ctx);
223 /* Call the command to get keys with pattern. */
224 reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
225 ASSERT_NOERROR(reply)
227 /* Prepare the arguments for the command. */
228 size_t items = RedisModule_CallReplyLength(reply);
230 //RedisModule_ReplyWithArray(ctx, items);
231 RedisModule_ReplyWithCallReply(ctx, reply);
232 RedisModule_FreeCallReply(reply);
235 RedisModuleString *cmdargv[items];
237 for (j = 0; j < items; j++) {
238 RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
241 /*Assume all keys via SDL is string type for sake of saving time*/
243 /*Check if key type is string*/
244 RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
247 int type = RedisModule_KeyType(key);
248 RedisModule_CloseKey(key);
249 if (type == REDISMODULE_KEYTYPE_STRING) {
253 RedisModule_CloseKey(key);
257 RedisModule_FreeCallReply(reply);
259 reply = RedisModule_Call(ctx, "MGET", "v", cmdargv, i);
260 ASSERT_NOERROR(reply)
261 items = RedisModule_CallReplyLength(reply);
262 RedisModule_ReplyWithArray(ctx, i*2);
263 for (j = 0; (j<items && j<i); j++) {
264 RedisModule_ReplyWithString(ctx, cmdargv[j]);
265 RedisModule_ReplyWithString(ctx, RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j)));
268 RedisModule_FreeCallReply(reply);
271 return REDISMODULE_OK;
274 int NDel_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
276 RedisModuleCallReply *reply = NULL;
279 return RedisModule_WrongArity(ctx);
281 /* Call the command to get keys with pattern. */
282 reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
283 ASSERT_NOERROR(reply)
285 /* Prepare the arguments for the command. */
286 size_t items = RedisModule_CallReplyLength(reply);
288 RedisModule_ReplyWithLongLong(ctx, 0);
289 RedisModule_FreeCallReply(reply);
292 RedisModuleString *cmdargv[items];
294 for (j = 0; j < items; j++) {
295 RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
298 /*Assume all keys via SDL is string type for sake of saving time*/
300 //Check if key type is string
301 RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
304 int type = RedisModule_KeyType(key);
305 RedisModule_CloseKey(key);
306 if (type == REDISMODULE_KEYTYPE_STRING) {
310 RedisModule_CloseKey(key);
314 RedisModule_FreeCallReply(reply);
316 reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, i);
317 ASSERT_NOERROR(reply)
318 RedisModule_ReplyWithCallReply(ctx, reply);
319 RedisModule_FreeCallReply(reply);
323 return REDISMODULE_OK;
326 int setPubStringCommon(RedisModuleCtx *ctx, SetParams* setParamsPtr, PubParams* pubParamsPtr)
328 RedisModuleCallReply *setReply;
329 setReply = RedisModule_Call(ctx, "MSET", "v!", setParamsPtr->key_val_pairs, setParamsPtr->length);
330 ASSERT_NOERROR(setReply)
331 int replytype = RedisModule_CallReplyType(setReply);
332 if (replytype == REDISMODULE_REPLY_NULL) {
333 RedisModule_ReplyWithNull(ctx);
335 multiPubCommand(ctx, pubParamsPtr);
336 RedisModule_ReplyWithCallReply(ctx, setReply);
338 RedisModule_FreeCallReply(setReply);
339 return REDISMODULE_OK;
342 int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
344 if (argc < 5 || (argc % 2) == 0)
345 return RedisModule_WrongArity(ctx);
347 SetParams setParams = {
348 .key_val_pairs = argv + 1,
351 PubParams pubParams = {
352 .channel_msg_pairs = argv + argc - 2,
356 return setPubStringCommon(ctx, &setParams, &pubParams);
359 int setIENEPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
361 if (argc < 6 || (argc % 2) != 0)
362 return RedisModule_WrongArity(ctx);
364 SetParams setParams = {
365 .key_val_pairs = argv + 1,
368 PubParams pubParams = {
369 .channel_msg_pairs = argv + 4,
372 RedisModuleString *key = setParams.key_val_pairs[0];
373 RedisModuleString *oldvalstr = argv[3];
375 int type = getKeyType(ctx, key);
376 if (flag == OBJ_OP_IE && type == REDISMODULE_KEYTYPE_EMPTY) {
377 return RedisModule_ReplyWithNull(ctx);
378 } else if (type != REDISMODULE_KEYTYPE_STRING && type != REDISMODULE_KEYTYPE_EMPTY) {
379 return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
382 RedisModuleCallReply *reply = RedisModule_Call(ctx, "GET", "s", key);
383 ASSERT_NOERROR(reply)
384 bool is_equal = replyContentsEqualString(reply, oldvalstr);
385 RedisModule_FreeCallReply(reply);
386 if ((flag == OBJ_OP_IE && !is_equal) ||
387 (flag == OBJ_OP_NE && is_equal)) {
388 return RedisModule_ReplyWithNull(ctx);
391 return setPubStringCommon(ctx, &setParams, &pubParams);
394 int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
396 return setIENEPubStringCommon(ctx, argv, argc, OBJ_OP_NE);
399 int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
401 return setIENEPubStringCommon(ctx, argv, argc, OBJ_OP_IE);
404 int setXXNXPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
406 if (argc < 5 || (argc % 2) == 0)
407 return RedisModule_WrongArity(ctx);
409 SetParams setParams = {
410 .key_val_pairs = argv + 1,
413 PubParams pubParams = {
414 .channel_msg_pairs = argv + 3,
417 RedisModuleString *key = setParams.key_val_pairs[0];
419 int type = getKeyType(ctx, key);
420 if ((flag == OBJ_OP_XX && type == REDISMODULE_KEYTYPE_EMPTY) ||
421 (flag == OBJ_OP_NX && type == REDISMODULE_KEYTYPE_STRING)) {
422 return RedisModule_ReplyWithNull(ctx);
423 } else if (type != REDISMODULE_KEYTYPE_STRING && type != REDISMODULE_KEYTYPE_EMPTY) {
424 RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
425 return REDISMODULE_OK;
428 return setPubStringCommon(ctx, &setParams, &pubParams);
431 int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
433 return setXXNXPubStringCommon(ctx, argv, argc, OBJ_OP_NX);
436 int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
438 return setXXNXPubStringCommon(ctx, argv, argc, OBJ_OP_XX);
441 int delPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
442 int argc, const int flag)
444 RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
445 RedisModuleCallReply *reply = NULL;
447 if (flag == OBJ_OP_NO) {
449 return RedisModule_WrongArity(ctx);
451 channel = argv[argc-2];
452 message = argv[argc-1];
456 return RedisModule_WrongArity(ctx);
464 if (flag != OBJ_OP_NO) {
465 /*Check if key type is string*/
466 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
468 int type = RedisModule_KeyType(key);
469 RedisModule_CloseKey(key);
471 if (type == REDISMODULE_KEYTYPE_EMPTY) {
472 return RedisModule_ReplyWithLongLong(ctx, 0);
473 } else if (type != REDISMODULE_KEYTYPE_STRING) {
474 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
478 if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
480 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
481 ASSERT_NOERROR(reply)
482 size_t curlen = 0, oldvallen = 0;
483 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
484 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
485 if (((flag == OBJ_OP_IE) &&
486 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
488 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
489 !strncmp(oldval, curval, curlen))) {
490 RedisModule_FreeCallReply(reply);
491 return RedisModule_ReplyWithLongLong(ctx, 0);
493 RedisModule_FreeCallReply(reply);
497 /* Prepare the arguments for the command. */
498 int i, j=0, cmdargc=argc-3;
499 RedisModuleString *cmdargv[cmdargc];
500 for (i = 1; i < argc-2; i++) {
501 if ((flag == OBJ_OP_IE || flag == OBJ_OP_NE) && (i == 2))
503 cmdargv[j++] = argv[i];
506 /* Call the command and pass back the reply. */
507 reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, j);
508 ASSERT_NOERROR(reply)
509 int replytype = RedisModule_CallReplyType(reply);
510 if (replytype == REDISMODULE_REPLY_NULL) {
511 RedisModule_ReplyWithNull(ctx);
513 else if (RedisModule_CallReplyInteger(reply) == 0) {
514 RedisModule_ReplyWithCallReply(ctx, reply);
517 cmdargv[0] = channel;
518 cmdargv[1] = message;
519 RedisModuleCallReply *pubreply = RedisModule_Call(ctx, "PUBLISH", "v", cmdargv, cmdargc);
520 RedisModule_FreeCallReply(pubreply);
521 RedisModule_ReplyWithCallReply(ctx, reply);
524 RedisModule_FreeCallReply(reply);
525 return REDISMODULE_OK;
528 int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
530 return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
533 int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
535 return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
538 int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
540 return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
543 /* This function must be present on each Redis module. It is used in order to
544 * register the commands into the Redis server. */
545 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
546 REDISMODULE_NOT_USED(argv);
547 REDISMODULE_NOT_USED(argc);
549 if (RedisModule_Init(ctx,"exstrings",1,REDISMODULE_APIVER_1)
550 == REDISMODULE_ERR) return REDISMODULE_ERR;
552 if (RedisModule_CreateCommand(ctx,"setie",
553 SetIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
554 return REDISMODULE_ERR;
556 if (RedisModule_CreateCommand(ctx,"setne",
557 SetNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
558 return REDISMODULE_ERR;
560 if (RedisModule_CreateCommand(ctx,"delie",
561 DelIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
562 return REDISMODULE_ERR;
564 if (RedisModule_CreateCommand(ctx,"delne",
565 DelNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
566 return REDISMODULE_ERR;
568 if (RedisModule_CreateCommand(ctx,"nget",
569 NGet_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
570 return REDISMODULE_ERR;
572 if (RedisModule_CreateCommand(ctx,"ndel",
573 NDel_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
574 return REDISMODULE_ERR;
576 if (RedisModule_CreateCommand(ctx,"msetpub",
577 SetPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
578 return REDISMODULE_ERR;
580 if (RedisModule_CreateCommand(ctx,"setiepub",
581 SetIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
582 return REDISMODULE_ERR;
584 if (RedisModule_CreateCommand(ctx,"setnepub",
585 SetNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
586 return REDISMODULE_ERR;
588 if (RedisModule_CreateCommand(ctx,"setxxpub",
589 SetXXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
590 return REDISMODULE_ERR;
592 if (RedisModule_CreateCommand(ctx,"setnxpub",
593 SetNXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
594 return REDISMODULE_ERR;
596 if (RedisModule_CreateCommand(ctx,"delpub",
597 DelPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
598 return REDISMODULE_ERR;
600 if (RedisModule_CreateCommand(ctx,"deliepub",
601 DelIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
602 return REDISMODULE_ERR;
604 if (RedisModule_CreateCommand(ctx,"delnepub",
605 DelNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
606 return REDISMODULE_ERR;
608 return REDISMODULE_OK;