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