Add base version of Redis modules
[ric-plt/dbaas.git] / redismodule / src / exstrings.c
diff --git a/redismodule/src/exstrings.c b/redismodule/src/exstrings.c
new file mode 100755 (executable)
index 0000000..66f31c2
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+ * Copyright (c) 2018-2019 Nokia.
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+#include "redismodule.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include "../../redismodule/include/redismodule.h"
+
+#ifdef __UT__
+#include "exstringsStub.h"
+#endif
+
+/* make sure the response is not NULL or an error.
+sends the error to the client and exit the current function if its */
+#define  ASSERT_NOERROR(r) \
+    if (r == NULL) { \
+        return RedisModule_ReplyWithError(ctx,"ERR reply is NULL"); \
+    } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \
+        RedisModule_ReplyWithCallReply(ctx,r); \
+        RedisModule_FreeCallReply(r); \
+        return REDISMODULE_ERR; \
+    }
+
+#define OBJ_OP_NO 0
+#define OBJ_OP_XX (1<<1)     /* OP if key exist */
+#define OBJ_OP_NX (1<<2)     /* OP if key not exist */
+#define OBJ_OP_IE (1<<4)     /* OP if equal old value */
+#define OBJ_OP_NE (1<<5)     /* OP if not equal old value */
+
+
+int setStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
+                                       int argc, const int flag)
+{
+    RedisModuleString *oldvalstr = NULL;
+    RedisModuleCallReply *reply = NULL;
+
+    if (argc < 4)
+        return RedisModule_WrongArity(ctx);
+    else
+        oldvalstr = argv[3];
+
+    /*Check if key type is string*/
+    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+        REDISMODULE_READ);
+    int type = RedisModule_KeyType(key);
+    RedisModule_CloseKey(key);
+
+    if (type == REDISMODULE_KEYTYPE_EMPTY) {
+        if (flag == OBJ_OP_IE){
+            RedisModule_ReplyWithNull(ctx);
+            return REDISMODULE_OK;
+        }
+    } else if (type != REDISMODULE_KEYTYPE_STRING) {
+        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+    }
+
+    /*Get the value*/
+    reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
+    ASSERT_NOERROR(reply)
+    size_t curlen=0, oldvallen=0;
+    const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
+    const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
+    if (((flag == OBJ_OP_IE) &&
+        (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
+        ||
+        ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
+          !strncmp(oldval, curval, curlen))) {
+        RedisModule_FreeCallReply(reply);
+        return RedisModule_ReplyWithNull(ctx);
+    }
+    RedisModule_FreeCallReply(reply);
+
+    /* Prepare the arguments for the command. */
+    int i, j=0, cmdargc=argc-2;
+    RedisModuleString *cmdargv[cmdargc];
+    for (i = 1; i < argc; i++) {
+        if (i == 3)
+            continue;
+        cmdargv[j++] = argv[i];
+    }
+
+    /* Call the command and pass back the reply. */
+    reply = RedisModule_Call(ctx, "SET", "v!", cmdargv, cmdargc);
+    ASSERT_NOERROR(reply)
+    RedisModule_ReplyWithCallReply(ctx, reply);
+
+    RedisModule_FreeCallReply(reply);
+    return REDISMODULE_OK;
+}
+
+int SetIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
+}
+
+int SetNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
+}
+
+int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
+                                       int argc, const int flag)
+{
+    RedisModuleString *oldvalstr = NULL;
+    RedisModuleCallReply *reply = NULL;
+
+    if (argc == 3)
+        oldvalstr = argv[2];
+    else
+        return RedisModule_WrongArity(ctx);
+
+    /*Check if key type is string*/
+    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+        REDISMODULE_READ);
+    int type = RedisModule_KeyType(key);
+    RedisModule_CloseKey(key);
+
+    if (type == REDISMODULE_KEYTYPE_EMPTY) {
+        return RedisModule_ReplyWithLongLong(ctx, 0);
+    } else if (type != REDISMODULE_KEYTYPE_STRING) {
+        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+    }
+
+    /*Get the value*/
+    reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
+    ASSERT_NOERROR(reply)
+    size_t curlen = 0, oldvallen = 0;
+    const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
+    const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
+    if (((flag == OBJ_OP_IE) &&
+        (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
+        ||
+        ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
+          !strncmp(oldval, curval, curlen))) {
+        RedisModule_FreeCallReply(reply);
+        return RedisModule_ReplyWithLongLong(ctx, 0);
+    }
+    RedisModule_FreeCallReply(reply);
+
+    /* Prepare the arguments for the command. */
+    int cmdargc=1;
+    RedisModuleString *cmdargv[1];
+    cmdargv[0] = argv[1];
+
+    /* Call the command and pass back the reply. */
+    reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, cmdargc);
+    ASSERT_NOERROR(reply)
+    RedisModule_ReplyWithCallReply(ctx, reply);
+
+    RedisModule_FreeCallReply(reply);
+    return REDISMODULE_OK;
+}
+
+int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return delStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
+}
+
+int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return delStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
+}
+
+int NGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    RedisModuleCallReply *reply = NULL;
+
+    if (argc != 2)
+        return RedisModule_WrongArity(ctx);
+
+    /* Call the command to get keys with pattern. */
+    reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
+    ASSERT_NOERROR(reply)
+
+    /* Prepare the arguments for the command. */
+    size_t items = RedisModule_CallReplyLength(reply);
+    if (items == 0) {
+        //RedisModule_ReplyWithArray(ctx, items);
+        RedisModule_ReplyWithCallReply(ctx, reply);
+        RedisModule_FreeCallReply(reply);
+    }
+    else {
+        RedisModuleString *cmdargv[items];
+        size_t i=0, j;
+        for (j = 0; j < items; j++) {
+           RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
+           cmdargv[i++] = rms;
+
+           /*Assume all keys via SDL is string type for sake of saving time*/
+#if 0
+           /*Check if key type is string*/
+           RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
+
+           if (key) {
+               int type = RedisModule_KeyType(key);
+               RedisModule_CloseKey(key);
+               if (type == REDISMODULE_KEYTYPE_STRING) {
+                   cmdargv[i++] = rms;
+               }
+           } else {
+               RedisModule_CloseKey(key);
+           }
+#endif
+        }
+        RedisModule_FreeCallReply(reply);
+
+        reply = RedisModule_Call(ctx, "MGET", "v", cmdargv, i);
+        ASSERT_NOERROR(reply)
+        items = RedisModule_CallReplyLength(reply);
+        RedisModule_ReplyWithArray(ctx, i*2);
+        for (j = 0; (j<items && j<i); j++) {
+           RedisModule_ReplyWithString(ctx, cmdargv[j]);
+           RedisModule_ReplyWithString(ctx, RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j)));
+        }
+
+        RedisModule_FreeCallReply(reply);
+    }
+
+    return REDISMODULE_OK;
+}
+
+int NDel_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    RedisModuleCallReply *reply = NULL;
+
+    if (argc != 2)
+        return RedisModule_WrongArity(ctx);
+
+    /* Call the command to get keys with pattern. */
+    reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
+    ASSERT_NOERROR(reply)
+
+    /* Prepare the arguments for the command. */
+    size_t items = RedisModule_CallReplyLength(reply);
+    if (items == 0) {
+        RedisModule_ReplyWithLongLong(ctx, 0);
+        RedisModule_FreeCallReply(reply);
+    }
+    else {
+        RedisModuleString *cmdargv[items];
+        size_t i=0, j;
+        for (j = 0; j < items; j++) {
+           RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
+           cmdargv[i++] = rms;
+
+           /*Assume all keys via SDL is string type for sake of saving time*/
+#if 0
+           //Check if key type is string
+           RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
+
+           if (key) {
+               int type = RedisModule_KeyType(key);
+               RedisModule_CloseKey(key);
+               if (type == REDISMODULE_KEYTYPE_STRING) {
+                   cmdargv[i++] = rms;
+               }
+           } else {
+               RedisModule_CloseKey(key);
+           }
+#endif
+        }
+        RedisModule_FreeCallReply(reply);
+
+        reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, i);
+        ASSERT_NOERROR(reply)
+        RedisModule_ReplyWithCallReply(ctx, reply);
+        RedisModule_FreeCallReply(reply);
+
+    }
+
+    return REDISMODULE_OK;
+}
+
+int setPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
+                                       int argc, const int flag)
+{
+    RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
+    RedisModuleCallReply *reply = NULL;
+
+    if (flag == OBJ_OP_NO) {
+        if (argc < 5 || (argc % 2) == 0)
+            return RedisModule_WrongArity(ctx);
+        else {
+            channel = argv[argc-2];
+            message = argv[argc-1];
+        }
+    } else if (flag == OBJ_OP_XX || flag == OBJ_OP_NX) {
+        if (argc != 5)
+            return RedisModule_WrongArity(ctx);
+        else {
+            channel = argv[3];
+            message = argv[4];
+        }
+    } else {
+        if (argc != 6)
+            return RedisModule_WrongArity(ctx);
+        else {
+            oldvalstr = argv[3];
+            channel = argv[4];
+            message = argv[5];
+        }
+    }
+
+    /*Check if key type is string*/
+    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+        REDISMODULE_READ);
+    int type = RedisModule_KeyType(key);
+    RedisModule_CloseKey(key);
+
+    if (flag != OBJ_OP_NO) {
+        if (type == REDISMODULE_KEYTYPE_EMPTY) {
+            if (flag == OBJ_OP_IE || flag == OBJ_OP_XX){
+                return RedisModule_ReplyWithNull(ctx);
+            }
+        } else if (flag == OBJ_OP_NX) {
+            return RedisModule_ReplyWithNull(ctx);
+        } else if (type != REDISMODULE_KEYTYPE_STRING) {
+            return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+        }
+    }
+
+    if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
+        /*Get the value*/
+        reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
+        ASSERT_NOERROR(reply)
+        size_t curlen = 0, oldvallen = 0;
+        const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
+        const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
+        if (((flag == OBJ_OP_IE) &&
+            (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
+            ||
+            ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
+              !strncmp(oldval, curval, curlen))) {
+            RedisModule_FreeCallReply(reply);
+            return RedisModule_ReplyWithNull(ctx);
+        }
+        RedisModule_FreeCallReply(reply);
+    }
+
+
+    /* Prepare the arguments for the command. */
+    int i, j=0, cmdargc=argc-3;
+    RedisModuleString *cmdargv[cmdargc];
+    for (i = 1; i < argc-2; i++) {
+        if ((flag == OBJ_OP_IE || flag == OBJ_OP_NE) && (i == 3))
+            continue;
+        cmdargv[j++] = argv[i];
+    }
+
+    /* Call the command and pass back the reply. */
+    reply = RedisModule_Call(ctx, "MSET", "v!", cmdargv, j);
+    ASSERT_NOERROR(reply)
+    int replytype = RedisModule_CallReplyType(reply);
+    if (replytype == REDISMODULE_REPLY_NULL) {
+        RedisModule_ReplyWithNull(ctx);
+    }
+    else {
+        cmdargc = 2;
+        cmdargv[0] = channel;
+        cmdargv[1] = message;
+        RedisModuleCallReply *pubreply = RedisModule_Call(ctx, "PUBLISH", "v", cmdargv, cmdargc);
+        RedisModule_FreeCallReply(pubreply);
+        RedisModule_ReplyWithCallReply(ctx, reply);
+    }
+
+    RedisModule_FreeCallReply(reply);
+    return REDISMODULE_OK;
+}
+
+int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
+}
+
+int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
+}
+
+int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
+}
+
+int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NX);
+}
+
+int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_XX);
+}
+
+int delPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
+                                       int argc, const int flag)
+{
+    RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
+    RedisModuleCallReply *reply = NULL;
+
+    if (flag == OBJ_OP_NO) {
+        if (argc < 4)
+            return RedisModule_WrongArity(ctx);
+        else {
+            channel = argv[argc-2];
+            message = argv[argc-1];
+        }
+    } else {
+        if (argc != 5)
+            return RedisModule_WrongArity(ctx);
+        else {
+            oldvalstr = argv[2];
+            channel = argv[3];
+            message = argv[4];
+        }
+    }
+
+    if (flag != OBJ_OP_NO) {
+        /*Check if key type is string*/
+        RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+            REDISMODULE_READ);
+        int type = RedisModule_KeyType(key);
+        RedisModule_CloseKey(key);
+
+        if (type == REDISMODULE_KEYTYPE_EMPTY) {
+            return RedisModule_ReplyWithLongLong(ctx, 0);
+        } else if (type != REDISMODULE_KEYTYPE_STRING) {
+            return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+        }
+    }
+
+    if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
+        /*Get the value*/
+        reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
+        ASSERT_NOERROR(reply)
+        size_t curlen = 0, oldvallen = 0;
+        const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
+        const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
+        if (((flag == OBJ_OP_IE) &&
+            (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
+            ||
+            ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
+              !strncmp(oldval, curval, curlen))) {
+            RedisModule_FreeCallReply(reply);
+            return RedisModule_ReplyWithLongLong(ctx, 0);
+        }
+        RedisModule_FreeCallReply(reply);
+    }
+
+
+    /* Prepare the arguments for the command. */
+    int i, j=0, cmdargc=argc-3;
+    RedisModuleString *cmdargv[cmdargc];
+    for (i = 1; i < argc-2; i++) {
+        if ((flag == OBJ_OP_IE || flag == OBJ_OP_NE) && (i == 2))
+            continue;
+        cmdargv[j++] = argv[i];
+    }
+
+    /* Call the command and pass back the reply. */
+    reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, j);
+    ASSERT_NOERROR(reply)
+    int replytype = RedisModule_CallReplyType(reply);
+    if (replytype == REDISMODULE_REPLY_NULL) {
+        RedisModule_ReplyWithNull(ctx);
+    }
+    else if (RedisModule_CallReplyInteger(reply) == 0) {
+        RedisModule_ReplyWithCallReply(ctx, reply);
+    } else {
+        cmdargc = 2;
+        cmdargv[0] = channel;
+        cmdargv[1] = message;
+        RedisModuleCallReply *pubreply = RedisModule_Call(ctx, "PUBLISH", "v", cmdargv, cmdargc);
+        RedisModule_FreeCallReply(pubreply);
+        RedisModule_ReplyWithCallReply(ctx, reply);
+    }
+
+    RedisModule_FreeCallReply(reply);
+    return REDISMODULE_OK;
+}
+
+int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+   return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
+}
+
+int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+   return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
+}
+
+int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+   return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
+}
+
+/* This function must be present on each Redis module. It is used in order to
+ * register the commands into the Redis server. */
+int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+    REDISMODULE_NOT_USED(argv);
+    REDISMODULE_NOT_USED(argc);
+
+    if (RedisModule_Init(ctx,"exstrings",1,REDISMODULE_APIVER_1)
+        == REDISMODULE_ERR) return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setie",
+        SetIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setne",
+        SetNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"delie",
+        DelIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"delne",
+        DelNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"nget",
+        NGet_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"ndel",
+        NDel_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"msetpub",
+        SetPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setiepub",
+        SetIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setnepub",
+        SetNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setxxpub",
+        SetXXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setnxpub",
+        SetNXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"delpub",
+        DelPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"deliepub",
+        DelIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"delnepub",
+        DelNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    return REDISMODULE_OK;
+}