From 0a650a5c5a1cee4953ddb5fb73b4f8209cf7c96a Mon Sep 17 00:00:00 2001 From: Evan Wies Date: Mon, 17 Jul 2017 05:45:56 +0000 Subject: [PATCH] add STRPOP (#12) Includes test and documentation. --- README.md | 9 +++++++ src/rxstrings.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/README.md b/README.md index 8817967..fa61867 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,15 @@ Credit: Meni Katz **Reply:** Integer, the length of the String after it was modified. +## `STRPOP key` + +Gets the value at `key`, deletes `key`, then returns the value. +It is equivalent to an atomic [GET](https://redis.io/commands/get) and [DEL](https://redis.io/commands/del). + + * An error is returned if the key exists and does not hold a string. + +**Reply:** String, the value at `key` or NULL if `key` didn't exist. + # rxhashes This module provides extended Redis Hashes commands. diff --git a/src/rxstrings.c b/src/rxstrings.c index ebf0b75..2951f9e 100644 --- a/src/rxstrings.c +++ b/src/rxstrings.c @@ -463,6 +463,49 @@ int SetRangeRandCommand(RedisModuleCtx *ctx, RedisModuleString **argv, return REDISMODULE_OK; } +/* +* STRPOP key +* Gets the value at key, deletes the key, then return the value. +* Equivalent to an atomic GET and DEL. +* An error is returned if there is a value stored that is not a string. +* String Reply: the value of key, or nil if key does not exist. +*/ +int StrPopCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc != 2) { + return RedisModule_WrongArity(ctx); + } + RedisModule_AutoMemory(ctx); + + /* Obtain key */ + RedisModuleKey *key = + RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); + + if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { + RedisModule_ReplyWithNull(ctx); + return REDISMODULE_OK; + } + + /* Key must be a string */ + if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_STRING) { + RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); + return REDISMODULE_ERR; + } + + /* GET the string */ + size_t dmaLen; + char* valPtr = RedisModule_StringDMA(key, &dmaLen, REDISMODULE_READ); + RedisModuleString* valStr = RedisModule_CreateString(ctx, valPtr, dmaLen); + + /* DELete the key */ + if (RedisModule_DeleteKey(key) != REDISMODULE_OK) { + RedisModule_ReplyWithError(ctx, "ERR DeleteKey failed"); + return REDISMODULE_ERR; + } + + RedisModule_ReplyWithString(ctx, valStr); + return REDISMODULE_OK; +} + int testCheckAnd(RedisModuleCtx *ctx) { RedisModuleCallReply *r; @@ -549,6 +592,22 @@ int testSetRangeRand(RedisModuleCtx *ctx) { return 0; } +int testStrPop(RedisModuleCtx *ctx) { + RedisModuleCallReply *r; + + r = RedisModule_Call(ctx, "set", "cc", "foo", "abcdef"); + RMUtil_AssertReplyEquals(r,"OK"); + r = RedisModule_Call(ctx, "strpop", "c", "foo"); + RMUtil_AssertReplyEquals(r,"abcdef"); + r = RedisModule_Call(ctx, "exists", "c", "foo"); + RMUtil_Assert(RedisModule_CallReplyInteger(r) == 0); + r = RedisModule_Call(ctx, "strpop", "c", "bar"); + RMUtil_Assert(RedisModule_CallReplyType(r) == REDISMODULE_REPLY_NULL); + r = RedisModule_Call(ctx, "FLUSHALL", ""); + + return 0; +} + int TestModule(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); @@ -564,6 +623,7 @@ int TestModule(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RMUtil_Test(testChop); RMUtil_Test(testPrepend); RMUtil_Test(testSetRangeRand); + RMUtil_Test(testStrPop); RedisModule_ReplyWithSimpleString(ctx, "PASS"); return REDISMODULE_OK; @@ -589,6 +649,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx) { "write fast deny-oom", 1, 1, 1) == REDISMODULE_ERR) return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx, "strpop", StrPopCommand, + "fast deny-oom", 1, 1, + 1) == REDISMODULE_ERR) + return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx, "rxstrings.test", TestModule, "write", 0, 0, 0) == REDISMODULE_ERR) return REDISMODULE_ERR;