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"
22 #include "../../redismodule/include/redismodule.h"
25 #include "exstringsStub.h"
28 /* make sure the response is not NULL or an error.
29 sends the error to the client and exit the current function if its */
30 #define ASSERT_NOERROR(r) \
32 return RedisModule_ReplyWithError(ctx,"ERR reply is NULL"); \
33 } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \
34 RedisModule_ReplyWithCallReply(ctx,r); \
35 RedisModule_FreeCallReply(r); \
36 return REDISMODULE_ERR; \
40 #define OBJ_OP_XX (1<<1) /* OP if key exist */
41 #define OBJ_OP_NX (1<<2) /* OP if key not exist */
42 #define OBJ_OP_IE (1<<4) /* OP if equal old value */
43 #define OBJ_OP_NE (1<<5) /* OP if not equal old value */
46 int setStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
47 int argc, const int flag)
49 RedisModuleString *oldvalstr = NULL;
50 RedisModuleCallReply *reply = NULL;
53 return RedisModule_WrongArity(ctx);
57 /*Check if key type is string*/
58 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
60 int type = RedisModule_KeyType(key);
61 RedisModule_CloseKey(key);
63 if (type == REDISMODULE_KEYTYPE_EMPTY) {
64 if (flag == OBJ_OP_IE){
65 RedisModule_ReplyWithNull(ctx);
66 return REDISMODULE_OK;
68 } else if (type != REDISMODULE_KEYTYPE_STRING) {
69 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
73 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
75 size_t curlen=0, oldvallen=0;
76 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
77 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
78 if (((flag == OBJ_OP_IE) &&
79 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
81 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
82 !strncmp(oldval, curval, curlen))) {
83 RedisModule_FreeCallReply(reply);
84 return RedisModule_ReplyWithNull(ctx);
86 RedisModule_FreeCallReply(reply);
88 /* Prepare the arguments for the command. */
89 int i, j=0, cmdargc=argc-2;
90 RedisModuleString *cmdargv[cmdargc];
91 for (i = 1; i < argc; i++) {
94 cmdargv[j++] = argv[i];
97 /* Call the command and pass back the reply. */
98 reply = RedisModule_Call(ctx, "SET", "v!", cmdargv, cmdargc);
100 RedisModule_ReplyWithCallReply(ctx, reply);
102 RedisModule_FreeCallReply(reply);
103 return REDISMODULE_OK;
106 int SetIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
108 return setStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
111 int SetNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
113 return setStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
116 int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
117 int argc, const int flag)
119 RedisModuleString *oldvalstr = NULL;
120 RedisModuleCallReply *reply = NULL;
125 return RedisModule_WrongArity(ctx);
127 /*Check if key type is string*/
128 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
130 int type = RedisModule_KeyType(key);
131 RedisModule_CloseKey(key);
133 if (type == REDISMODULE_KEYTYPE_EMPTY) {
134 return RedisModule_ReplyWithLongLong(ctx, 0);
135 } else if (type != REDISMODULE_KEYTYPE_STRING) {
136 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
140 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
141 ASSERT_NOERROR(reply)
142 size_t curlen = 0, oldvallen = 0;
143 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
144 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
145 if (((flag == OBJ_OP_IE) &&
146 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
148 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
149 !strncmp(oldval, curval, curlen))) {
150 RedisModule_FreeCallReply(reply);
151 return RedisModule_ReplyWithLongLong(ctx, 0);
153 RedisModule_FreeCallReply(reply);
155 /* Prepare the arguments for the command. */
157 RedisModuleString *cmdargv[1];
158 cmdargv[0] = argv[1];
160 /* Call the command and pass back the reply. */
161 reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, cmdargc);
162 ASSERT_NOERROR(reply)
163 RedisModule_ReplyWithCallReply(ctx, reply);
165 RedisModule_FreeCallReply(reply);
166 return REDISMODULE_OK;
169 int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
171 return delStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
174 int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
176 return delStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
179 int NGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
181 RedisModuleCallReply *reply = NULL;
184 return RedisModule_WrongArity(ctx);
186 /* Call the command to get keys with pattern. */
187 reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
188 ASSERT_NOERROR(reply)
190 /* Prepare the arguments for the command. */
191 size_t items = RedisModule_CallReplyLength(reply);
193 //RedisModule_ReplyWithArray(ctx, items);
194 RedisModule_ReplyWithCallReply(ctx, reply);
195 RedisModule_FreeCallReply(reply);
198 RedisModuleString *cmdargv[items];
200 for (j = 0; j < items; j++) {
201 RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
204 /*Assume all keys via SDL is string type for sake of saving time*/
206 /*Check if key type is string*/
207 RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
210 int type = RedisModule_KeyType(key);
211 RedisModule_CloseKey(key);
212 if (type == REDISMODULE_KEYTYPE_STRING) {
216 RedisModule_CloseKey(key);
220 RedisModule_FreeCallReply(reply);
222 reply = RedisModule_Call(ctx, "MGET", "v", cmdargv, i);
223 ASSERT_NOERROR(reply)
224 items = RedisModule_CallReplyLength(reply);
225 RedisModule_ReplyWithArray(ctx, i*2);
226 for (j = 0; (j<items && j<i); j++) {
227 RedisModule_ReplyWithString(ctx, cmdargv[j]);
228 RedisModule_ReplyWithString(ctx, RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j)));
231 RedisModule_FreeCallReply(reply);
234 return REDISMODULE_OK;
237 int NDel_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
239 RedisModuleCallReply *reply = NULL;
242 return RedisModule_WrongArity(ctx);
244 /* Call the command to get keys with pattern. */
245 reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
246 ASSERT_NOERROR(reply)
248 /* Prepare the arguments for the command. */
249 size_t items = RedisModule_CallReplyLength(reply);
251 RedisModule_ReplyWithLongLong(ctx, 0);
252 RedisModule_FreeCallReply(reply);
255 RedisModuleString *cmdargv[items];
257 for (j = 0; j < items; j++) {
258 RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
261 /*Assume all keys via SDL is string type for sake of saving time*/
263 //Check if key type is string
264 RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
267 int type = RedisModule_KeyType(key);
268 RedisModule_CloseKey(key);
269 if (type == REDISMODULE_KEYTYPE_STRING) {
273 RedisModule_CloseKey(key);
277 RedisModule_FreeCallReply(reply);
279 reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, i);
280 ASSERT_NOERROR(reply)
281 RedisModule_ReplyWithCallReply(ctx, reply);
282 RedisModule_FreeCallReply(reply);
286 return REDISMODULE_OK;
289 int setPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
290 int argc, const int flag)
292 RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
293 RedisModuleCallReply *reply = NULL;
295 if (flag == OBJ_OP_NO) {
296 if (argc < 5 || (argc % 2) == 0)
297 return RedisModule_WrongArity(ctx);
299 channel = argv[argc-2];
300 message = argv[argc-1];
302 } else if (flag == OBJ_OP_XX || flag == OBJ_OP_NX) {
304 return RedisModule_WrongArity(ctx);
311 return RedisModule_WrongArity(ctx);
319 /*Check if key type is string*/
320 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
322 int type = RedisModule_KeyType(key);
323 RedisModule_CloseKey(key);
325 if (flag != OBJ_OP_NO) {
326 if (type == REDISMODULE_KEYTYPE_EMPTY) {
327 if (flag == OBJ_OP_IE || flag == OBJ_OP_XX){
328 return RedisModule_ReplyWithNull(ctx);
330 } else if (flag == OBJ_OP_NX) {
331 return RedisModule_ReplyWithNull(ctx);
332 } else if (type != REDISMODULE_KEYTYPE_STRING) {
333 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
337 if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
339 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
340 ASSERT_NOERROR(reply)
341 size_t curlen = 0, oldvallen = 0;
342 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
343 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
344 if (((flag == OBJ_OP_IE) &&
345 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
347 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
348 !strncmp(oldval, curval, curlen))) {
349 RedisModule_FreeCallReply(reply);
350 return RedisModule_ReplyWithNull(ctx);
352 RedisModule_FreeCallReply(reply);
356 /* Prepare the arguments for the command. */
357 int i, j=0, cmdargc=argc-3;
358 RedisModuleString *cmdargv[cmdargc];
359 for (i = 1; i < argc-2; i++) {
360 if ((flag == OBJ_OP_IE || flag == OBJ_OP_NE) && (i == 3))
362 cmdargv[j++] = argv[i];
365 /* Call the command and pass back the reply. */
366 reply = RedisModule_Call(ctx, "MSET", "v!", cmdargv, j);
367 ASSERT_NOERROR(reply)
368 int replytype = RedisModule_CallReplyType(reply);
369 if (replytype == REDISMODULE_REPLY_NULL) {
370 RedisModule_ReplyWithNull(ctx);
374 cmdargv[0] = channel;
375 cmdargv[1] = message;
376 RedisModuleCallReply *pubreply = RedisModule_Call(ctx, "PUBLISH", "v", cmdargv, cmdargc);
377 RedisModule_FreeCallReply(pubreply);
378 RedisModule_ReplyWithCallReply(ctx, reply);
381 RedisModule_FreeCallReply(reply);
382 return REDISMODULE_OK;
385 int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
387 return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
390 int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
392 return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
395 int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
397 return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
400 int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
402 return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NX);
405 int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
407 return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_XX);
410 int delPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
411 int argc, const int flag)
413 RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
414 RedisModuleCallReply *reply = NULL;
416 if (flag == OBJ_OP_NO) {
418 return RedisModule_WrongArity(ctx);
420 channel = argv[argc-2];
421 message = argv[argc-1];
425 return RedisModule_WrongArity(ctx);
433 if (flag != OBJ_OP_NO) {
434 /*Check if key type is string*/
435 RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
437 int type = RedisModule_KeyType(key);
438 RedisModule_CloseKey(key);
440 if (type == REDISMODULE_KEYTYPE_EMPTY) {
441 return RedisModule_ReplyWithLongLong(ctx, 0);
442 } else if (type != REDISMODULE_KEYTYPE_STRING) {
443 return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
447 if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
449 reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
450 ASSERT_NOERROR(reply)
451 size_t curlen = 0, oldvallen = 0;
452 const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
453 const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
454 if (((flag == OBJ_OP_IE) &&
455 (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
457 ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
458 !strncmp(oldval, curval, curlen))) {
459 RedisModule_FreeCallReply(reply);
460 return RedisModule_ReplyWithLongLong(ctx, 0);
462 RedisModule_FreeCallReply(reply);
466 /* Prepare the arguments for the command. */
467 int i, j=0, cmdargc=argc-3;
468 RedisModuleString *cmdargv[cmdargc];
469 for (i = 1; i < argc-2; i++) {
470 if ((flag == OBJ_OP_IE || flag == OBJ_OP_NE) && (i == 2))
472 cmdargv[j++] = argv[i];
475 /* Call the command and pass back the reply. */
476 reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, j);
477 ASSERT_NOERROR(reply)
478 int replytype = RedisModule_CallReplyType(reply);
479 if (replytype == REDISMODULE_REPLY_NULL) {
480 RedisModule_ReplyWithNull(ctx);
482 else if (RedisModule_CallReplyInteger(reply) == 0) {
483 RedisModule_ReplyWithCallReply(ctx, reply);
486 cmdargv[0] = channel;
487 cmdargv[1] = message;
488 RedisModuleCallReply *pubreply = RedisModule_Call(ctx, "PUBLISH", "v", cmdargv, cmdargc);
489 RedisModule_FreeCallReply(pubreply);
490 RedisModule_ReplyWithCallReply(ctx, reply);
493 RedisModule_FreeCallReply(reply);
494 return REDISMODULE_OK;
497 int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
499 return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
502 int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
504 return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
507 int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
509 return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
512 /* This function must be present on each Redis module. It is used in order to
513 * register the commands into the Redis server. */
514 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
515 REDISMODULE_NOT_USED(argv);
516 REDISMODULE_NOT_USED(argc);
518 if (RedisModule_Init(ctx,"exstrings",1,REDISMODULE_APIVER_1)
519 == REDISMODULE_ERR) return REDISMODULE_ERR;
521 if (RedisModule_CreateCommand(ctx,"setie",
522 SetIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
523 return REDISMODULE_ERR;
525 if (RedisModule_CreateCommand(ctx,"setne",
526 SetNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
527 return REDISMODULE_ERR;
529 if (RedisModule_CreateCommand(ctx,"delie",
530 DelIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
531 return REDISMODULE_ERR;
533 if (RedisModule_CreateCommand(ctx,"delne",
534 DelNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
535 return REDISMODULE_ERR;
537 if (RedisModule_CreateCommand(ctx,"nget",
538 NGet_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
539 return REDISMODULE_ERR;
541 if (RedisModule_CreateCommand(ctx,"ndel",
542 NDel_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
543 return REDISMODULE_ERR;
545 if (RedisModule_CreateCommand(ctx,"msetpub",
546 SetPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
547 return REDISMODULE_ERR;
549 if (RedisModule_CreateCommand(ctx,"setiepub",
550 SetIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
551 return REDISMODULE_ERR;
553 if (RedisModule_CreateCommand(ctx,"setnepub",
554 SetNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
555 return REDISMODULE_ERR;
557 if (RedisModule_CreateCommand(ctx,"setxxpub",
558 SetXXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
559 return REDISMODULE_ERR;
561 if (RedisModule_CreateCommand(ctx,"setnxpub",
562 SetNXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
563 return REDISMODULE_ERR;
565 if (RedisModule_CreateCommand(ctx,"delpub",
566 DelPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
567 return REDISMODULE_ERR;
569 if (RedisModule_CreateCommand(ctx,"deliepub",
570 DelIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
571 return REDISMODULE_ERR;
573 if (RedisModule_CreateCommand(ctx,"delnepub",
574 DelNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
575 return REDISMODULE_ERR;
577 return REDISMODULE_OK;