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