-
Notifications
You must be signed in to change notification settings - Fork 206
Expand file tree
/
Copy pathmodule.c
More file actions
147 lines (118 loc) · 4.53 KB
/
module.c
File metadata and controls
147 lines (118 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#define REDISMODULE_MAIN
#define REDISMODULE_EXPERIMENTAL_API
#include "redismodule.h"
#include "rmutil/util.h"
#include "rmutil/strings.h"
#include "rmutil/test_util.h"
/* EXAMPLE.PARSE [SUM <x> <y>] | [PROD <x> <y>]
* Demonstrates the automatic arg parsing utility.
* If the command receives "SUM <x> <y>" it returns their sum
* If it receives "PROD <x> <y>" it returns their product
*/
int ParseCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
// we must have at least 4 args
if (argc < 4) {
return RedisModule_WrongArity(ctx);
}
// init auto memory for created strings
RedisModule_AutoMemory(ctx);
long long x, y;
// If we got SUM - return the sum of 2 consecutive arguments
if (RMUtil_ParseArgsAfter("SUM", argv, argc, "ll", &x, &y) ==
REDISMODULE_OK) {
RedisModule_ReplyWithLongLong(ctx, x + y);
return REDISMODULE_OK;
}
// If we got PROD - return the product of 2 consecutive arguments
if (RMUtil_ParseArgsAfter("PROD", argv, argc, "ll", &x, &y) ==
REDISMODULE_OK) {
RedisModule_ReplyWithLongLong(ctx, x * y);
return REDISMODULE_OK;
}
// something is fishy...
RedisModule_ReplyWithError(ctx, "Invalid arguments");
return REDISMODULE_ERR;
}
/*
* example.HGETSET <key> <element> <value>
* Atomically set a value in a HASH key to <value> and return its value before
* the HSET.
*
* Basically atomic HGET + HSET
*/
int HGetSetCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
// we need EXACTLY 4 arguments
if (argc != 4) {
return RedisModule_WrongArity(ctx);
}
RedisModule_AutoMemory(ctx);
// open the key and make sure it's indeed a HASH and not empty
RedisModuleKey *key =
RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE);
if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_HASH &&
RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_EMPTY) {
return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
}
// get the current value of the hash element
RedisModuleCallReply *rep =
RedisModule_Call(ctx, "HGET", "ss", argv[1], argv[2]);
RMUTIL_ASSERT_NOERROR(ctx, rep);
// set the new value of the element
RedisModuleCallReply *srep =
RedisModule_Call(ctx, "HSET", "sss", argv[1], argv[2], argv[3]);
RMUTIL_ASSERT_NOERROR(ctx, srep);
// if the value was null before - we just return null
if (RedisModule_CallReplyType(rep) == REDISMODULE_REPLY_NULL) {
RedisModule_ReplyWithNull(ctx);
return REDISMODULE_OK;
}
// forward the HGET reply to the client
RedisModule_ReplyWithCallReply(ctx, rep);
return REDISMODULE_OK;
}
// Test the the PARSE command
int testParse(RedisModuleCtx *ctx) {
RedisModuleCallReply *r =
RedisModule_Call(ctx, "example.parse", "ccc", "SUM", "5", "2");
RMUtil_Assert(RedisModule_CallReplyType(r) == REDISMODULE_REPLY_INTEGER);
RMUtil_AssertReplyEquals(r, "7");
r = RedisModule_Call(ctx, "example.parse", "ccc", "PROD", "5", "2");
RMUtil_Assert(RedisModule_CallReplyType(r) == REDISMODULE_REPLY_INTEGER);
RMUtil_AssertReplyEquals(r, "10");
return 0;
}
// test the HGETSET command
int testHgetSet(RedisModuleCtx *ctx) {
RedisModuleCallReply *r =
RedisModule_Call(ctx, "example.hgetset", "ccc", "foo", "bar", "baz");
RMUtil_Assert(RedisModule_CallReplyType(r) != REDISMODULE_REPLY_ERROR);
r = RedisModule_Call(ctx, "example.hgetset", "ccc", "foo", "bar", "bag");
RMUtil_Assert(RedisModule_CallReplyType(r) == REDISMODULE_REPLY_STRING);
RMUtil_AssertReplyEquals(r, "baz");
r = RedisModule_Call(ctx, "example.hgetset", "ccc", "foo", "bar", "bang");
RMUtil_AssertReplyEquals(r, "bag");
return 0;
}
// Unit test entry point for the module
int TestModule(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
RedisModule_AutoMemory(ctx);
RMUtil_Test(testParse);
RMUtil_Test(testHgetSet);
RedisModule_ReplyWithSimpleString(ctx, "PASS");
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx) {
// Register the module itself
if (RedisModule_Init(ctx, "example", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
// register example.parse - the default registration syntax
if (RedisModule_CreateCommand(ctx, "example.parse", ParseCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
// register example.hgetset - using the shortened utility registration macro
RMUtil_RegisterWriteCmd(ctx, "example.hgetset", HGetSetCommand);
// register the unit test
RMUtil_RegisterWriteCmd(ctx, "example.test", TestModule);
return REDISMODULE_OK;
}