Add new redis command MSETMPUB
[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 <stdbool.h>
23 #include "../../redismodule/include/redismodule.h"
24
25 #ifdef __UT__
26 #include "exstringsStub.h"
27 #endif
28
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) \
32     if (r == NULL) { \
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; \
38     }
39
40 #define OBJ_OP_NO 0
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 */
45
46 int getKeyType(RedisModuleCtx *ctx, RedisModuleString *key_str)
47 {
48     RedisModuleKey *key = RedisModule_OpenKey(ctx, key_str, REDISMODULE_READ);
49     int type = RedisModule_KeyType(key);
50     RedisModule_CloseKey(key);
51     return type;
52 }
53
54 bool replyContentsEqualString(RedisModuleCallReply *reply, RedisModuleString *expected_value)
55 {
56     size_t replylen = 0, expectedlen = 0;
57     const char *expectedval = RedisModule_StringPtrLen(expected_value, &expectedlen);
58     const char *replyval = RedisModule_CallReplyStringPtr(reply, &replylen);
59     return replyval &&
60            expectedlen == replylen &&
61            !strncmp(expectedval, replyval, replylen);
62 }
63
64 typedef struct _SetParams {
65     RedisModuleString **key_val_pairs;
66     size_t length;
67 } SetParams;
68
69 typedef struct _PubParams {
70     RedisModuleString **channel_msg_pairs;
71     size_t length;
72 } PubParams;
73
74 void multiPubCommand(RedisModuleCtx *ctx, PubParams* pubParams)
75 {
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);
80     }
81 }
82
83 int setStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
84                                        int argc, const int flag)
85 {
86     RedisModuleString *oldvalstr = NULL;
87     RedisModuleCallReply *reply = NULL;
88
89     if (argc < 4)
90         return RedisModule_WrongArity(ctx);
91     else
92         oldvalstr = argv[3];
93
94     /*Check if key type is string*/
95     RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
96         REDISMODULE_READ);
97     int type = RedisModule_KeyType(key);
98     RedisModule_CloseKey(key);
99
100     if (type == REDISMODULE_KEYTYPE_EMPTY) {
101         if (flag == OBJ_OP_IE){
102             RedisModule_ReplyWithNull(ctx);
103             return REDISMODULE_OK;
104         }
105     } else if (type != REDISMODULE_KEYTYPE_STRING) {
106         return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
107     }
108
109     /*Get the value*/
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)))
117         ||
118         ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
119           !strncmp(oldval, curval, curlen))) {
120         RedisModule_FreeCallReply(reply);
121         return RedisModule_ReplyWithNull(ctx);
122     }
123     RedisModule_FreeCallReply(reply);
124
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++) {
129         if (i == 3)
130             continue;
131         cmdargv[j++] = argv[i];
132     }
133
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);
138
139     RedisModule_FreeCallReply(reply);
140     return REDISMODULE_OK;
141 }
142
143 int SetIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
144 {
145     return setStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
146 }
147
148 int SetNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
149 {
150     return setStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
151 }
152
153 int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
154                                        int argc, const int flag)
155 {
156     RedisModuleString *oldvalstr = NULL;
157     RedisModuleCallReply *reply = NULL;
158
159     if (argc == 3)
160         oldvalstr = argv[2];
161     else
162         return RedisModule_WrongArity(ctx);
163
164     /*Check if key type is string*/
165     RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
166         REDISMODULE_READ);
167     int type = RedisModule_KeyType(key);
168     RedisModule_CloseKey(key);
169
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);
174     }
175
176     /*Get the value*/
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)))
184         ||
185         ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
186           !strncmp(oldval, curval, curlen))) {
187         RedisModule_FreeCallReply(reply);
188         return RedisModule_ReplyWithLongLong(ctx, 0);
189     }
190     RedisModule_FreeCallReply(reply);
191
192     /* Prepare the arguments for the command. */
193     int cmdargc=1;
194     RedisModuleString *cmdargv[1];
195     cmdargv[0] = argv[1];
196
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);
201
202     RedisModule_FreeCallReply(reply);
203     return REDISMODULE_OK;
204 }
205
206 int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
207 {
208     return delStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
209 }
210
211 int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
212 {
213     return delStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
214 }
215
216 int NGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
217 {
218     RedisModuleCallReply *reply = NULL;
219
220     if (argc != 2)
221         return RedisModule_WrongArity(ctx);
222
223     /* Call the command to get keys with pattern. */
224     reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
225     ASSERT_NOERROR(reply)
226
227     /* Prepare the arguments for the command. */
228     size_t items = RedisModule_CallReplyLength(reply);
229     if (items == 0) {
230         //RedisModule_ReplyWithArray(ctx, items);
231         RedisModule_ReplyWithCallReply(ctx, reply);
232         RedisModule_FreeCallReply(reply);
233     }
234     else {
235         RedisModuleString *cmdargv[items];
236         size_t i=0, j;
237         for (j = 0; j < items; j++) {
238            RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
239            cmdargv[i++] = rms;
240
241            /*Assume all keys via SDL is string type for sake of saving time*/
242 #if 0
243            /*Check if key type is string*/
244            RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
245
246            if (key) {
247                int type = RedisModule_KeyType(key);
248                RedisModule_CloseKey(key);
249                if (type == REDISMODULE_KEYTYPE_STRING) {
250                    cmdargv[i++] = rms;
251                }
252            } else {
253                RedisModule_CloseKey(key);
254            }
255 #endif
256         }
257         RedisModule_FreeCallReply(reply);
258
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)));
266         }
267
268         RedisModule_FreeCallReply(reply);
269     }
270
271     return REDISMODULE_OK;
272 }
273
274 int NDel_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
275 {
276     RedisModuleCallReply *reply = NULL;
277
278     if (argc != 2)
279         return RedisModule_WrongArity(ctx);
280
281     /* Call the command to get keys with pattern. */
282     reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
283     ASSERT_NOERROR(reply)
284
285     /* Prepare the arguments for the command. */
286     size_t items = RedisModule_CallReplyLength(reply);
287     if (items == 0) {
288         RedisModule_ReplyWithLongLong(ctx, 0);
289         RedisModule_FreeCallReply(reply);
290     }
291     else {
292         RedisModuleString *cmdargv[items];
293         size_t i=0, j;
294         for (j = 0; j < items; j++) {
295            RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
296            cmdargv[i++] = rms;
297
298            /*Assume all keys via SDL is string type for sake of saving time*/
299 #if 0
300            //Check if key type is string
301            RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
302
303            if (key) {
304                int type = RedisModule_KeyType(key);
305                RedisModule_CloseKey(key);
306                if (type == REDISMODULE_KEYTYPE_STRING) {
307                    cmdargv[i++] = rms;
308                }
309            } else {
310                RedisModule_CloseKey(key);
311            }
312 #endif
313         }
314         RedisModule_FreeCallReply(reply);
315
316         reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, i);
317         ASSERT_NOERROR(reply)
318         RedisModule_ReplyWithCallReply(ctx, reply);
319         RedisModule_FreeCallReply(reply);
320
321     }
322
323     return REDISMODULE_OK;
324 }
325
326 int setPubStringCommon(RedisModuleCtx *ctx, SetParams* setParamsPtr, PubParams* pubParamsPtr)
327 {
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);
334     } else {
335         multiPubCommand(ctx, pubParamsPtr);
336         RedisModule_ReplyWithCallReply(ctx, setReply);
337     }
338     RedisModule_FreeCallReply(setReply);
339     return REDISMODULE_OK;
340 }
341
342 int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
343 {
344     if (argc < 5 || (argc % 2) == 0)
345         return RedisModule_WrongArity(ctx);
346
347     SetParams setParams = {
348                            .key_val_pairs = argv + 1,
349                            .length = argc - 3
350                           };
351     PubParams pubParams = {
352                            .channel_msg_pairs = argv + argc - 2,
353                            .length = 2
354                           };
355
356     return setPubStringCommon(ctx, &setParams, &pubParams);
357 }
358
359 int SetMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
360 {
361     if (argc < 7 || (argc % 2) == 0)
362         return RedisModule_WrongArity(ctx);
363
364     long long setPairsCount, pubPairsCount;
365     RedisModule_StringToLongLong(argv[1], &setPairsCount);
366     RedisModule_StringToLongLong(argv[2], &pubPairsCount);
367     if (setPairsCount < 1 || pubPairsCount < 1)
368         return RedisModule_ReplyWithError(ctx, "ERR SET_PAIR_COUNT and PUB_PAIR_COUNT must be greater than zero");
369
370     long long setLen, pubLen;
371     setLen = 2*setPairsCount;
372     pubLen = 2*pubPairsCount;
373     if (setLen + pubLen + 3 != argc)
374         return RedisModule_ReplyWithError(ctx, "ERR SET_PAIR_COUNT or PUB_PAIR_COUNT do not match the total pair count");
375
376     SetParams setParams = {
377                            .key_val_pairs = argv + 3,
378                            .length = setLen
379                           };
380     PubParams pubParams = {
381                            .channel_msg_pairs = argv + 3 + setParams.length,
382                            .length = pubLen
383                           };
384
385     return setPubStringCommon(ctx, &setParams, &pubParams);
386 }
387
388 int setIENEPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
389 {
390     if (argc < 6 || (argc % 2) != 0)
391         return RedisModule_WrongArity(ctx);
392
393     SetParams setParams = {
394                            .key_val_pairs = argv + 1,
395                            .length = 2
396                           };
397     PubParams pubParams = {
398                            .channel_msg_pairs = argv + 4,
399                            .length = argc - 4
400                           };
401     RedisModuleString *key = setParams.key_val_pairs[0];
402     RedisModuleString *oldvalstr = argv[3];
403
404     int type = getKeyType(ctx, key);
405     if (flag == OBJ_OP_IE && type == REDISMODULE_KEYTYPE_EMPTY) {
406         return RedisModule_ReplyWithNull(ctx);
407     } else if (type != REDISMODULE_KEYTYPE_STRING && type != REDISMODULE_KEYTYPE_EMPTY) {
408         return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
409     }
410
411     RedisModuleCallReply *reply = RedisModule_Call(ctx, "GET", "s", key);
412     ASSERT_NOERROR(reply)
413     bool is_equal = replyContentsEqualString(reply, oldvalstr);
414     RedisModule_FreeCallReply(reply);
415     if ((flag == OBJ_OP_IE && !is_equal) ||
416         (flag == OBJ_OP_NE && is_equal)) {
417         return RedisModule_ReplyWithNull(ctx);
418     }
419
420     return setPubStringCommon(ctx, &setParams, &pubParams);
421 }
422
423 int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
424 {
425     return setIENEPubStringCommon(ctx, argv, argc, OBJ_OP_NE);
426 }
427
428 int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
429 {
430     return setIENEPubStringCommon(ctx, argv, argc, OBJ_OP_IE);
431 }
432
433 int setXXNXPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
434 {
435     if (argc < 5 || (argc % 2) == 0)
436         return RedisModule_WrongArity(ctx);
437
438     SetParams setParams = {
439                            .key_val_pairs = argv + 1,
440                            .length = 2
441                           };
442     PubParams pubParams = {
443                            .channel_msg_pairs = argv + 3,
444                            .length = argc - 3
445                           };
446     RedisModuleString *key = setParams.key_val_pairs[0];
447
448     int type = getKeyType(ctx, key);
449     if ((flag == OBJ_OP_XX && type == REDISMODULE_KEYTYPE_EMPTY) ||
450         (flag == OBJ_OP_NX && type == REDISMODULE_KEYTYPE_STRING)) {
451         return RedisModule_ReplyWithNull(ctx);
452     } else if (type != REDISMODULE_KEYTYPE_STRING && type != REDISMODULE_KEYTYPE_EMPTY) {
453         RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
454         return REDISMODULE_OK;
455     }
456
457     return setPubStringCommon(ctx, &setParams, &pubParams);
458 }
459
460 int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
461 {
462     return setXXNXPubStringCommon(ctx, argv, argc, OBJ_OP_NX);
463 }
464
465 int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
466 {
467     return setXXNXPubStringCommon(ctx, argv, argc, OBJ_OP_XX);
468 }
469
470 int delPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
471                                        int argc, const int flag)
472 {
473     RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
474     RedisModuleCallReply *reply = NULL;
475
476     if (flag == OBJ_OP_NO) {
477         if (argc < 4)
478             return RedisModule_WrongArity(ctx);
479         else {
480             channel = argv[argc-2];
481             message = argv[argc-1];
482         }
483     } else {
484         if (argc != 5)
485             return RedisModule_WrongArity(ctx);
486         else {
487             oldvalstr = argv[2];
488             channel = argv[3];
489             message = argv[4];
490         }
491     }
492
493     if (flag != OBJ_OP_NO) {
494         /*Check if key type is string*/
495         RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
496             REDISMODULE_READ);
497         int type = RedisModule_KeyType(key);
498         RedisModule_CloseKey(key);
499
500         if (type == REDISMODULE_KEYTYPE_EMPTY) {
501             return RedisModule_ReplyWithLongLong(ctx, 0);
502         } else if (type != REDISMODULE_KEYTYPE_STRING) {
503             return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
504         }
505     }
506
507     if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
508         /*Get the value*/
509         reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
510         ASSERT_NOERROR(reply)
511         size_t curlen = 0, oldvallen = 0;
512         const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
513         const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
514         if (((flag == OBJ_OP_IE) &&
515             (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
516             ||
517             ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
518               !strncmp(oldval, curval, curlen))) {
519             RedisModule_FreeCallReply(reply);
520             return RedisModule_ReplyWithLongLong(ctx, 0);
521         }
522         RedisModule_FreeCallReply(reply);
523     }
524
525
526     /* Prepare the arguments for the command. */
527     int i, j=0, cmdargc=argc-3;
528     RedisModuleString *cmdargv[cmdargc];
529     for (i = 1; i < argc-2; i++) {
530         if ((flag == OBJ_OP_IE || flag == OBJ_OP_NE) && (i == 2))
531             continue;
532         cmdargv[j++] = argv[i];
533     }
534
535     /* Call the command and pass back the reply. */
536     reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, j);
537     ASSERT_NOERROR(reply)
538     int replytype = RedisModule_CallReplyType(reply);
539     if (replytype == REDISMODULE_REPLY_NULL) {
540         RedisModule_ReplyWithNull(ctx);
541     }
542     else if (RedisModule_CallReplyInteger(reply) == 0) {
543         RedisModule_ReplyWithCallReply(ctx, reply);
544     } else {
545         cmdargc = 2;
546         cmdargv[0] = channel;
547         cmdargv[1] = message;
548         RedisModuleCallReply *pubreply = RedisModule_Call(ctx, "PUBLISH", "v", cmdargv, cmdargc);
549         RedisModule_FreeCallReply(pubreply);
550         RedisModule_ReplyWithCallReply(ctx, reply);
551     }
552
553     RedisModule_FreeCallReply(reply);
554     return REDISMODULE_OK;
555 }
556
557 int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
558 {
559    return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
560 }
561
562 int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
563 {
564    return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
565 }
566
567 int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
568 {
569    return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
570 }
571
572 /* This function must be present on each Redis module. It is used in order to
573  * register the commands into the Redis server. */
574 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
575     REDISMODULE_NOT_USED(argv);
576     REDISMODULE_NOT_USED(argc);
577
578     if (RedisModule_Init(ctx,"exstrings",1,REDISMODULE_APIVER_1)
579         == REDISMODULE_ERR) return REDISMODULE_ERR;
580
581     if (RedisModule_CreateCommand(ctx,"setie",
582         SetIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
583         return REDISMODULE_ERR;
584
585     if (RedisModule_CreateCommand(ctx,"setne",
586         SetNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
587         return REDISMODULE_ERR;
588
589     if (RedisModule_CreateCommand(ctx,"delie",
590         DelIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
591         return REDISMODULE_ERR;
592
593     if (RedisModule_CreateCommand(ctx,"delne",
594         DelNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
595         return REDISMODULE_ERR;
596
597     if (RedisModule_CreateCommand(ctx,"nget",
598         NGet_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
599         return REDISMODULE_ERR;
600
601     if (RedisModule_CreateCommand(ctx,"ndel",
602         NDel_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
603         return REDISMODULE_ERR;
604
605     if (RedisModule_CreateCommand(ctx,"msetpub",
606         SetPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
607         return REDISMODULE_ERR;
608
609     if (RedisModule_CreateCommand(ctx,"msetmpub",
610         SetMPub_RedisCommand,"write deny-oom pubsub",1,1,1) == REDISMODULE_ERR)
611         return REDISMODULE_ERR;
612
613     if (RedisModule_CreateCommand(ctx,"setiepub",
614         SetIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
615         return REDISMODULE_ERR;
616
617     if (RedisModule_CreateCommand(ctx,"setnepub",
618         SetNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
619         return REDISMODULE_ERR;
620
621     if (RedisModule_CreateCommand(ctx,"setxxpub",
622         SetXXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
623         return REDISMODULE_ERR;
624
625     if (RedisModule_CreateCommand(ctx,"setnxpub",
626         SetNXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
627         return REDISMODULE_ERR;
628
629     if (RedisModule_CreateCommand(ctx,"delpub",
630         DelPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
631         return REDISMODULE_ERR;
632
633     if (RedisModule_CreateCommand(ctx,"deliepub",
634         DelIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
635         return REDISMODULE_ERR;
636
637     if (RedisModule_CreateCommand(ctx,"delnepub",
638         DelNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
639         return REDISMODULE_ERR;
640
641     return REDISMODULE_OK;
642 }