Add base version of Redis modules
[ric-plt/dbaas.git] / redismodule / src / exstrings.c
1 /*
2  * Copyright (c) 2018-2019 Nokia.
3  *
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
7  *
8  *       http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "redismodule.h"
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include "../../redismodule/include/redismodule.h"
23
24 #ifdef __UT__
25 #include "exstringsStub.h"
26 #endif
27
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) \
31     if (r == NULL) { \
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; \
37     }
38
39 #define OBJ_OP_NO 0
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 */
44
45
46 int setStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
47                                        int argc, const int flag)
48 {
49     RedisModuleString *oldvalstr = NULL;
50     RedisModuleCallReply *reply = NULL;
51
52     if (argc < 4)
53         return RedisModule_WrongArity(ctx);
54     else
55         oldvalstr = argv[3];
56
57     /*Check if key type is string*/
58     RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
59         REDISMODULE_READ);
60     int type = RedisModule_KeyType(key);
61     RedisModule_CloseKey(key);
62
63     if (type == REDISMODULE_KEYTYPE_EMPTY) {
64         if (flag == OBJ_OP_IE){
65             RedisModule_ReplyWithNull(ctx);
66             return REDISMODULE_OK;
67         }
68     } else if (type != REDISMODULE_KEYTYPE_STRING) {
69         return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
70     }
71
72     /*Get the value*/
73     reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
74     ASSERT_NOERROR(reply)
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)))
80         ||
81         ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
82           !strncmp(oldval, curval, curlen))) {
83         RedisModule_FreeCallReply(reply);
84         return RedisModule_ReplyWithNull(ctx);
85     }
86     RedisModule_FreeCallReply(reply);
87
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++) {
92         if (i == 3)
93             continue;
94         cmdargv[j++] = argv[i];
95     }
96
97     /* Call the command and pass back the reply. */
98     reply = RedisModule_Call(ctx, "SET", "v!", cmdargv, cmdargc);
99     ASSERT_NOERROR(reply)
100     RedisModule_ReplyWithCallReply(ctx, reply);
101
102     RedisModule_FreeCallReply(reply);
103     return REDISMODULE_OK;
104 }
105
106 int SetIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
107 {
108     return setStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
109 }
110
111 int SetNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
112 {
113     return setStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
114 }
115
116 int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
117                                        int argc, const int flag)
118 {
119     RedisModuleString *oldvalstr = NULL;
120     RedisModuleCallReply *reply = NULL;
121
122     if (argc == 3)
123         oldvalstr = argv[2];
124     else
125         return RedisModule_WrongArity(ctx);
126
127     /*Check if key type is string*/
128     RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
129         REDISMODULE_READ);
130     int type = RedisModule_KeyType(key);
131     RedisModule_CloseKey(key);
132
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);
137     }
138
139     /*Get the value*/
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)))
147         ||
148         ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
149           !strncmp(oldval, curval, curlen))) {
150         RedisModule_FreeCallReply(reply);
151         return RedisModule_ReplyWithLongLong(ctx, 0);
152     }
153     RedisModule_FreeCallReply(reply);
154
155     /* Prepare the arguments for the command. */
156     int cmdargc=1;
157     RedisModuleString *cmdargv[1];
158     cmdargv[0] = argv[1];
159
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);
164
165     RedisModule_FreeCallReply(reply);
166     return REDISMODULE_OK;
167 }
168
169 int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
170 {
171     return delStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
172 }
173
174 int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
175 {
176     return delStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
177 }
178
179 int NGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
180 {
181     RedisModuleCallReply *reply = NULL;
182
183     if (argc != 2)
184         return RedisModule_WrongArity(ctx);
185
186     /* Call the command to get keys with pattern. */
187     reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
188     ASSERT_NOERROR(reply)
189
190     /* Prepare the arguments for the command. */
191     size_t items = RedisModule_CallReplyLength(reply);
192     if (items == 0) {
193         //RedisModule_ReplyWithArray(ctx, items);
194         RedisModule_ReplyWithCallReply(ctx, reply);
195         RedisModule_FreeCallReply(reply);
196     }
197     else {
198         RedisModuleString *cmdargv[items];
199         size_t i=0, j;
200         for (j = 0; j < items; j++) {
201            RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
202            cmdargv[i++] = rms;
203
204            /*Assume all keys via SDL is string type for sake of saving time*/
205 #if 0
206            /*Check if key type is string*/
207            RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
208
209            if (key) {
210                int type = RedisModule_KeyType(key);
211                RedisModule_CloseKey(key);
212                if (type == REDISMODULE_KEYTYPE_STRING) {
213                    cmdargv[i++] = rms;
214                }
215            } else {
216                RedisModule_CloseKey(key);
217            }
218 #endif
219         }
220         RedisModule_FreeCallReply(reply);
221
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)));
229         }
230
231         RedisModule_FreeCallReply(reply);
232     }
233
234     return REDISMODULE_OK;
235 }
236
237 int NDel_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
238 {
239     RedisModuleCallReply *reply = NULL;
240
241     if (argc != 2)
242         return RedisModule_WrongArity(ctx);
243
244     /* Call the command to get keys with pattern. */
245     reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
246     ASSERT_NOERROR(reply)
247
248     /* Prepare the arguments for the command. */
249     size_t items = RedisModule_CallReplyLength(reply);
250     if (items == 0) {
251         RedisModule_ReplyWithLongLong(ctx, 0);
252         RedisModule_FreeCallReply(reply);
253     }
254     else {
255         RedisModuleString *cmdargv[items];
256         size_t i=0, j;
257         for (j = 0; j < items; j++) {
258            RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
259            cmdargv[i++] = rms;
260
261            /*Assume all keys via SDL is string type for sake of saving time*/
262 #if 0
263            //Check if key type is string
264            RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
265
266            if (key) {
267                int type = RedisModule_KeyType(key);
268                RedisModule_CloseKey(key);
269                if (type == REDISMODULE_KEYTYPE_STRING) {
270                    cmdargv[i++] = rms;
271                }
272            } else {
273                RedisModule_CloseKey(key);
274            }
275 #endif
276         }
277         RedisModule_FreeCallReply(reply);
278
279         reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, i);
280         ASSERT_NOERROR(reply)
281         RedisModule_ReplyWithCallReply(ctx, reply);
282         RedisModule_FreeCallReply(reply);
283
284     }
285
286     return REDISMODULE_OK;
287 }
288
289 int setPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
290                                        int argc, const int flag)
291 {
292     RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
293     RedisModuleCallReply *reply = NULL;
294
295     if (flag == OBJ_OP_NO) {
296         if (argc < 5 || (argc % 2) == 0)
297             return RedisModule_WrongArity(ctx);
298         else {
299             channel = argv[argc-2];
300             message = argv[argc-1];
301         }
302     } else if (flag == OBJ_OP_XX || flag == OBJ_OP_NX) {
303         if (argc != 5)
304             return RedisModule_WrongArity(ctx);
305         else {
306             channel = argv[3];
307             message = argv[4];
308         }
309     } else {
310         if (argc != 6)
311             return RedisModule_WrongArity(ctx);
312         else {
313             oldvalstr = argv[3];
314             channel = argv[4];
315             message = argv[5];
316         }
317     }
318
319     /*Check if key type is string*/
320     RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
321         REDISMODULE_READ);
322     int type = RedisModule_KeyType(key);
323     RedisModule_CloseKey(key);
324
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);
329             }
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);
334         }
335     }
336
337     if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
338         /*Get the value*/
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)))
346             ||
347             ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
348               !strncmp(oldval, curval, curlen))) {
349             RedisModule_FreeCallReply(reply);
350             return RedisModule_ReplyWithNull(ctx);
351         }
352         RedisModule_FreeCallReply(reply);
353     }
354
355
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))
361             continue;
362         cmdargv[j++] = argv[i];
363     }
364
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);
371     }
372     else {
373         cmdargc = 2;
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);
379     }
380
381     RedisModule_FreeCallReply(reply);
382     return REDISMODULE_OK;
383 }
384
385 int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
386 {
387     return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
388 }
389
390 int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
391 {
392     return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
393 }
394
395 int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
396 {
397     return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
398 }
399
400 int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
401 {
402     return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NX);
403 }
404
405 int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
406 {
407     return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_XX);
408 }
409
410 int delPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
411                                        int argc, const int flag)
412 {
413     RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
414     RedisModuleCallReply *reply = NULL;
415
416     if (flag == OBJ_OP_NO) {
417         if (argc < 4)
418             return RedisModule_WrongArity(ctx);
419         else {
420             channel = argv[argc-2];
421             message = argv[argc-1];
422         }
423     } else {
424         if (argc != 5)
425             return RedisModule_WrongArity(ctx);
426         else {
427             oldvalstr = argv[2];
428             channel = argv[3];
429             message = argv[4];
430         }
431     }
432
433     if (flag != OBJ_OP_NO) {
434         /*Check if key type is string*/
435         RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
436             REDISMODULE_READ);
437         int type = RedisModule_KeyType(key);
438         RedisModule_CloseKey(key);
439
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);
444         }
445     }
446
447     if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
448         /*Get the value*/
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)))
456             ||
457             ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
458               !strncmp(oldval, curval, curlen))) {
459             RedisModule_FreeCallReply(reply);
460             return RedisModule_ReplyWithLongLong(ctx, 0);
461         }
462         RedisModule_FreeCallReply(reply);
463     }
464
465
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))
471             continue;
472         cmdargv[j++] = argv[i];
473     }
474
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);
481     }
482     else if (RedisModule_CallReplyInteger(reply) == 0) {
483         RedisModule_ReplyWithCallReply(ctx, reply);
484     } else {
485         cmdargc = 2;
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);
491     }
492
493     RedisModule_FreeCallReply(reply);
494     return REDISMODULE_OK;
495 }
496
497 int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
498 {
499    return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
500 }
501
502 int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
503 {
504    return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
505 }
506
507 int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
508 {
509    return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
510 }
511
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);
517
518     if (RedisModule_Init(ctx,"exstrings",1,REDISMODULE_APIVER_1)
519         == REDISMODULE_ERR) return REDISMODULE_ERR;
520
521     if (RedisModule_CreateCommand(ctx,"setie",
522         SetIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
523         return REDISMODULE_ERR;
524
525     if (RedisModule_CreateCommand(ctx,"setne",
526         SetNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
527         return REDISMODULE_ERR;
528
529     if (RedisModule_CreateCommand(ctx,"delie",
530         DelIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
531         return REDISMODULE_ERR;
532
533     if (RedisModule_CreateCommand(ctx,"delne",
534         DelNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
535         return REDISMODULE_ERR;
536
537     if (RedisModule_CreateCommand(ctx,"nget",
538         NGet_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
539         return REDISMODULE_ERR;
540
541     if (RedisModule_CreateCommand(ctx,"ndel",
542         NDel_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
543         return REDISMODULE_ERR;
544
545     if (RedisModule_CreateCommand(ctx,"msetpub",
546         SetPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
547         return REDISMODULE_ERR;
548
549     if (RedisModule_CreateCommand(ctx,"setiepub",
550         SetIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
551         return REDISMODULE_ERR;
552
553     if (RedisModule_CreateCommand(ctx,"setnepub",
554         SetNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
555         return REDISMODULE_ERR;
556
557     if (RedisModule_CreateCommand(ctx,"setxxpub",
558         SetXXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
559         return REDISMODULE_ERR;
560
561     if (RedisModule_CreateCommand(ctx,"setnxpub",
562         SetNXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
563         return REDISMODULE_ERR;
564
565     if (RedisModule_CreateCommand(ctx,"delpub",
566         DelPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
567         return REDISMODULE_ERR;
568
569     if (RedisModule_CreateCommand(ctx,"deliepub",
570         DelIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
571         return REDISMODULE_ERR;
572
573     if (RedisModule_CreateCommand(ctx,"delnepub",
574         DelNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
575         return REDISMODULE_ERR;
576
577     return REDISMODULE_OK;
578 }