1*572c4311Sfengbojiang /* Helloworld module -- A few examples of the Redis Modules API in the form
2*572c4311Sfengbojiang * of commands showing how to accomplish common tasks.
3*572c4311Sfengbojiang *
4*572c4311Sfengbojiang * This module does not do anything useful, if not for a few commands. The
5*572c4311Sfengbojiang * examples are designed in order to show the API.
6*572c4311Sfengbojiang *
7*572c4311Sfengbojiang * -----------------------------------------------------------------------------
8*572c4311Sfengbojiang *
9*572c4311Sfengbojiang * Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
10*572c4311Sfengbojiang * All rights reserved.
11*572c4311Sfengbojiang *
12*572c4311Sfengbojiang * Redistribution and use in source and binary forms, with or without
13*572c4311Sfengbojiang * modification, are permitted provided that the following conditions are met:
14*572c4311Sfengbojiang *
15*572c4311Sfengbojiang * * Redistributions of source code must retain the above copyright notice,
16*572c4311Sfengbojiang * this list of conditions and the following disclaimer.
17*572c4311Sfengbojiang * * Redistributions in binary form must reproduce the above copyright
18*572c4311Sfengbojiang * notice, this list of conditions and the following disclaimer in the
19*572c4311Sfengbojiang * documentation and/or other materials provided with the distribution.
20*572c4311Sfengbojiang * * Neither the name of Redis nor the names of its contributors may be used
21*572c4311Sfengbojiang * to endorse or promote products derived from this software without
22*572c4311Sfengbojiang * specific prior written permission.
23*572c4311Sfengbojiang *
24*572c4311Sfengbojiang * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25*572c4311Sfengbojiang * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26*572c4311Sfengbojiang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27*572c4311Sfengbojiang * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28*572c4311Sfengbojiang * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29*572c4311Sfengbojiang * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30*572c4311Sfengbojiang * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31*572c4311Sfengbojiang * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32*572c4311Sfengbojiang * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33*572c4311Sfengbojiang * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34*572c4311Sfengbojiang * POSSIBILITY OF SUCH DAMAGE.
35*572c4311Sfengbojiang */
36*572c4311Sfengbojiang
37*572c4311Sfengbojiang #include "../redismodule.h"
38*572c4311Sfengbojiang #include <stdio.h>
39*572c4311Sfengbojiang #include <stdlib.h>
40*572c4311Sfengbojiang #include <ctype.h>
41*572c4311Sfengbojiang #include <string.h>
42*572c4311Sfengbojiang
43*572c4311Sfengbojiang /* HELLO.SIMPLE is among the simplest commands you can implement.
44*572c4311Sfengbojiang * It just returns the currently selected DB id, a functionality which is
45*572c4311Sfengbojiang * missing in Redis. The command uses two important API calls: one to
46*572c4311Sfengbojiang * fetch the currently selected DB, the other in order to send the client
47*572c4311Sfengbojiang * an integer reply as response. */
HelloSimple_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)48*572c4311Sfengbojiang int HelloSimple_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
49*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
50*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
51*572c4311Sfengbojiang RedisModule_ReplyWithLongLong(ctx,RedisModule_GetSelectedDb(ctx));
52*572c4311Sfengbojiang return REDISMODULE_OK;
53*572c4311Sfengbojiang }
54*572c4311Sfengbojiang
55*572c4311Sfengbojiang /* HELLO.PUSH.NATIVE re-implements RPUSH, and shows the low level modules API
56*572c4311Sfengbojiang * where you can "open" keys, make low level operations, create new keys by
57*572c4311Sfengbojiang * pushing elements into non-existing keys, and so forth.
58*572c4311Sfengbojiang *
59*572c4311Sfengbojiang * You'll find this command to be roughly as fast as the actual RPUSH
60*572c4311Sfengbojiang * command. */
HelloPushNative_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)61*572c4311Sfengbojiang int HelloPushNative_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
62*572c4311Sfengbojiang {
63*572c4311Sfengbojiang if (argc != 3) return RedisModule_WrongArity(ctx);
64*572c4311Sfengbojiang
65*572c4311Sfengbojiang RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
66*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
67*572c4311Sfengbojiang
68*572c4311Sfengbojiang RedisModule_ListPush(key,REDISMODULE_LIST_TAIL,argv[2]);
69*572c4311Sfengbojiang size_t newlen = RedisModule_ValueLength(key);
70*572c4311Sfengbojiang RedisModule_CloseKey(key);
71*572c4311Sfengbojiang RedisModule_ReplyWithLongLong(ctx,newlen);
72*572c4311Sfengbojiang return REDISMODULE_OK;
73*572c4311Sfengbojiang }
74*572c4311Sfengbojiang
75*572c4311Sfengbojiang /* HELLO.PUSH.CALL implements RPUSH using an higher level approach, calling
76*572c4311Sfengbojiang * a Redis command instead of working with the key in a low level way. This
77*572c4311Sfengbojiang * approach is useful when you need to call Redis commands that are not
78*572c4311Sfengbojiang * available as low level APIs, or when you don't need the maximum speed
79*572c4311Sfengbojiang * possible but instead prefer implementation simplicity. */
HelloPushCall_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)80*572c4311Sfengbojiang int HelloPushCall_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
81*572c4311Sfengbojiang {
82*572c4311Sfengbojiang if (argc != 3) return RedisModule_WrongArity(ctx);
83*572c4311Sfengbojiang
84*572c4311Sfengbojiang RedisModuleCallReply *reply;
85*572c4311Sfengbojiang
86*572c4311Sfengbojiang reply = RedisModule_Call(ctx,"RPUSH","ss",argv[1],argv[2]);
87*572c4311Sfengbojiang long long len = RedisModule_CallReplyInteger(reply);
88*572c4311Sfengbojiang RedisModule_FreeCallReply(reply);
89*572c4311Sfengbojiang RedisModule_ReplyWithLongLong(ctx,len);
90*572c4311Sfengbojiang return REDISMODULE_OK;
91*572c4311Sfengbojiang }
92*572c4311Sfengbojiang
93*572c4311Sfengbojiang /* HELLO.PUSH.CALL2
94*572c4311Sfengbojiang * This is exaxctly as HELLO.PUSH.CALL, but shows how we can reply to the
95*572c4311Sfengbojiang * client using directly a reply object that Call() returned. */
HelloPushCall2_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)96*572c4311Sfengbojiang int HelloPushCall2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
97*572c4311Sfengbojiang {
98*572c4311Sfengbojiang if (argc != 3) return RedisModule_WrongArity(ctx);
99*572c4311Sfengbojiang
100*572c4311Sfengbojiang RedisModuleCallReply *reply;
101*572c4311Sfengbojiang
102*572c4311Sfengbojiang reply = RedisModule_Call(ctx,"RPUSH","ss",argv[1],argv[2]);
103*572c4311Sfengbojiang RedisModule_ReplyWithCallReply(ctx,reply);
104*572c4311Sfengbojiang RedisModule_FreeCallReply(reply);
105*572c4311Sfengbojiang return REDISMODULE_OK;
106*572c4311Sfengbojiang }
107*572c4311Sfengbojiang
108*572c4311Sfengbojiang /* HELLO.LIST.SUM.LEN returns the total length of all the items inside
109*572c4311Sfengbojiang * a Redis list, by using the high level Call() API.
110*572c4311Sfengbojiang * This command is an example of the array reply access. */
HelloListSumLen_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)111*572c4311Sfengbojiang int HelloListSumLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
112*572c4311Sfengbojiang {
113*572c4311Sfengbojiang if (argc != 2) return RedisModule_WrongArity(ctx);
114*572c4311Sfengbojiang
115*572c4311Sfengbojiang RedisModuleCallReply *reply;
116*572c4311Sfengbojiang
117*572c4311Sfengbojiang reply = RedisModule_Call(ctx,"LRANGE","sll",argv[1],(long long)0,(long long)-1);
118*572c4311Sfengbojiang size_t strlen = 0;
119*572c4311Sfengbojiang size_t items = RedisModule_CallReplyLength(reply);
120*572c4311Sfengbojiang size_t j;
121*572c4311Sfengbojiang for (j = 0; j < items; j++) {
122*572c4311Sfengbojiang RedisModuleCallReply *ele = RedisModule_CallReplyArrayElement(reply,j);
123*572c4311Sfengbojiang strlen += RedisModule_CallReplyLength(ele);
124*572c4311Sfengbojiang }
125*572c4311Sfengbojiang RedisModule_FreeCallReply(reply);
126*572c4311Sfengbojiang RedisModule_ReplyWithLongLong(ctx,strlen);
127*572c4311Sfengbojiang return REDISMODULE_OK;
128*572c4311Sfengbojiang }
129*572c4311Sfengbojiang
130*572c4311Sfengbojiang /* HELLO.LIST.SPLICE srclist dstlist count
131*572c4311Sfengbojiang * Moves 'count' elements from the tail of 'srclist' to the head of
132*572c4311Sfengbojiang * 'dstlist'. If less than count elements are available, it moves as much
133*572c4311Sfengbojiang * elements as possible. */
HelloListSplice_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)134*572c4311Sfengbojiang int HelloListSplice_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
135*572c4311Sfengbojiang if (argc != 4) return RedisModule_WrongArity(ctx);
136*572c4311Sfengbojiang
137*572c4311Sfengbojiang RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1],
138*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
139*572c4311Sfengbojiang RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2],
140*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
141*572c4311Sfengbojiang
142*572c4311Sfengbojiang /* Src and dst key must be empty or lists. */
143*572c4311Sfengbojiang if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST &&
144*572c4311Sfengbojiang RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) ||
145*572c4311Sfengbojiang (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST &&
146*572c4311Sfengbojiang RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY))
147*572c4311Sfengbojiang {
148*572c4311Sfengbojiang RedisModule_CloseKey(srckey);
149*572c4311Sfengbojiang RedisModule_CloseKey(dstkey);
150*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
151*572c4311Sfengbojiang }
152*572c4311Sfengbojiang
153*572c4311Sfengbojiang long long count;
154*572c4311Sfengbojiang if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) ||
155*572c4311Sfengbojiang (count < 0)) {
156*572c4311Sfengbojiang RedisModule_CloseKey(srckey);
157*572c4311Sfengbojiang RedisModule_CloseKey(dstkey);
158*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,"ERR invalid count");
159*572c4311Sfengbojiang }
160*572c4311Sfengbojiang
161*572c4311Sfengbojiang while(count-- > 0) {
162*572c4311Sfengbojiang RedisModuleString *ele;
163*572c4311Sfengbojiang
164*572c4311Sfengbojiang ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL);
165*572c4311Sfengbojiang if (ele == NULL) break;
166*572c4311Sfengbojiang RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele);
167*572c4311Sfengbojiang RedisModule_FreeString(ctx,ele);
168*572c4311Sfengbojiang }
169*572c4311Sfengbojiang
170*572c4311Sfengbojiang size_t len = RedisModule_ValueLength(srckey);
171*572c4311Sfengbojiang RedisModule_CloseKey(srckey);
172*572c4311Sfengbojiang RedisModule_CloseKey(dstkey);
173*572c4311Sfengbojiang RedisModule_ReplyWithLongLong(ctx,len);
174*572c4311Sfengbojiang return REDISMODULE_OK;
175*572c4311Sfengbojiang }
176*572c4311Sfengbojiang
177*572c4311Sfengbojiang /* Like the HELLO.LIST.SPLICE above, but uses automatic memory management
178*572c4311Sfengbojiang * in order to avoid freeing stuff. */
HelloListSpliceAuto_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)179*572c4311Sfengbojiang int HelloListSpliceAuto_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
180*572c4311Sfengbojiang if (argc != 4) return RedisModule_WrongArity(ctx);
181*572c4311Sfengbojiang
182*572c4311Sfengbojiang RedisModule_AutoMemory(ctx);
183*572c4311Sfengbojiang
184*572c4311Sfengbojiang RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1],
185*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
186*572c4311Sfengbojiang RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2],
187*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
188*572c4311Sfengbojiang
189*572c4311Sfengbojiang /* Src and dst key must be empty or lists. */
190*572c4311Sfengbojiang if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST &&
191*572c4311Sfengbojiang RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) ||
192*572c4311Sfengbojiang (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST &&
193*572c4311Sfengbojiang RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY))
194*572c4311Sfengbojiang {
195*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
196*572c4311Sfengbojiang }
197*572c4311Sfengbojiang
198*572c4311Sfengbojiang long long count;
199*572c4311Sfengbojiang if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) ||
200*572c4311Sfengbojiang (count < 0))
201*572c4311Sfengbojiang {
202*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,"ERR invalid count");
203*572c4311Sfengbojiang }
204*572c4311Sfengbojiang
205*572c4311Sfengbojiang while(count-- > 0) {
206*572c4311Sfengbojiang RedisModuleString *ele;
207*572c4311Sfengbojiang
208*572c4311Sfengbojiang ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL);
209*572c4311Sfengbojiang if (ele == NULL) break;
210*572c4311Sfengbojiang RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele);
211*572c4311Sfengbojiang }
212*572c4311Sfengbojiang
213*572c4311Sfengbojiang size_t len = RedisModule_ValueLength(srckey);
214*572c4311Sfengbojiang RedisModule_ReplyWithLongLong(ctx,len);
215*572c4311Sfengbojiang return REDISMODULE_OK;
216*572c4311Sfengbojiang }
217*572c4311Sfengbojiang
218*572c4311Sfengbojiang /* HELLO.RAND.ARRAY <count>
219*572c4311Sfengbojiang * Shows how to generate arrays as commands replies.
220*572c4311Sfengbojiang * It just outputs <count> random numbers. */
HelloRandArray_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)221*572c4311Sfengbojiang int HelloRandArray_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
222*572c4311Sfengbojiang if (argc != 2) return RedisModule_WrongArity(ctx);
223*572c4311Sfengbojiang long long count;
224*572c4311Sfengbojiang if (RedisModule_StringToLongLong(argv[1],&count) != REDISMODULE_OK ||
225*572c4311Sfengbojiang count < 0)
226*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,"ERR invalid count");
227*572c4311Sfengbojiang
228*572c4311Sfengbojiang /* To reply with an array, we call RedisModule_ReplyWithArray() followed
229*572c4311Sfengbojiang * by other "count" calls to other reply functions in order to generate
230*572c4311Sfengbojiang * the elements of the array. */
231*572c4311Sfengbojiang RedisModule_ReplyWithArray(ctx,count);
232*572c4311Sfengbojiang while(count--) RedisModule_ReplyWithLongLong(ctx,rand());
233*572c4311Sfengbojiang return REDISMODULE_OK;
234*572c4311Sfengbojiang }
235*572c4311Sfengbojiang
236*572c4311Sfengbojiang /* This is a simple command to test replication. Because of the "!" modified
237*572c4311Sfengbojiang * in the RedisModule_Call() call, the two INCRs get replicated.
238*572c4311Sfengbojiang * Also note how the ECHO is replicated in an unexpected position (check
239*572c4311Sfengbojiang * comments the function implementation). */
HelloRepl1_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)240*572c4311Sfengbojiang int HelloRepl1_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
241*572c4311Sfengbojiang {
242*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
243*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
244*572c4311Sfengbojiang RedisModule_AutoMemory(ctx);
245*572c4311Sfengbojiang
246*572c4311Sfengbojiang /* This will be replicated *after* the two INCR statements, since
247*572c4311Sfengbojiang * the Call() replication has precedence, so the actual replication
248*572c4311Sfengbojiang * stream will be:
249*572c4311Sfengbojiang *
250*572c4311Sfengbojiang * MULTI
251*572c4311Sfengbojiang * INCR foo
252*572c4311Sfengbojiang * INCR bar
253*572c4311Sfengbojiang * ECHO c foo
254*572c4311Sfengbojiang * EXEC
255*572c4311Sfengbojiang */
256*572c4311Sfengbojiang RedisModule_Replicate(ctx,"ECHO","c","foo");
257*572c4311Sfengbojiang
258*572c4311Sfengbojiang /* Using the "!" modifier we replicate the command if it
259*572c4311Sfengbojiang * modified the dataset in some way. */
260*572c4311Sfengbojiang RedisModule_Call(ctx,"INCR","c!","foo");
261*572c4311Sfengbojiang RedisModule_Call(ctx,"INCR","c!","bar");
262*572c4311Sfengbojiang
263*572c4311Sfengbojiang RedisModule_ReplyWithLongLong(ctx,0);
264*572c4311Sfengbojiang
265*572c4311Sfengbojiang return REDISMODULE_OK;
266*572c4311Sfengbojiang }
267*572c4311Sfengbojiang
268*572c4311Sfengbojiang /* Another command to show replication. In this case, we call
269*572c4311Sfengbojiang * RedisModule_ReplicateVerbatim() to mean we want just the command to be
270*572c4311Sfengbojiang * propagated to slaves / AOF exactly as it was called by the user.
271*572c4311Sfengbojiang *
272*572c4311Sfengbojiang * This command also shows how to work with string objects.
273*572c4311Sfengbojiang * It takes a list, and increments all the elements (that must have
274*572c4311Sfengbojiang * a numerical value) by 1, returning the sum of all the elements
275*572c4311Sfengbojiang * as reply.
276*572c4311Sfengbojiang *
277*572c4311Sfengbojiang * Usage: HELLO.REPL2 <list-key> */
HelloRepl2_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)278*572c4311Sfengbojiang int HelloRepl2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
279*572c4311Sfengbojiang if (argc != 2) return RedisModule_WrongArity(ctx);
280*572c4311Sfengbojiang
281*572c4311Sfengbojiang RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
282*572c4311Sfengbojiang RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
283*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
284*572c4311Sfengbojiang
285*572c4311Sfengbojiang if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST)
286*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
287*572c4311Sfengbojiang
288*572c4311Sfengbojiang size_t listlen = RedisModule_ValueLength(key);
289*572c4311Sfengbojiang long long sum = 0;
290*572c4311Sfengbojiang
291*572c4311Sfengbojiang /* Rotate and increment. */
292*572c4311Sfengbojiang while(listlen--) {
293*572c4311Sfengbojiang RedisModuleString *ele = RedisModule_ListPop(key,REDISMODULE_LIST_TAIL);
294*572c4311Sfengbojiang long long val;
295*572c4311Sfengbojiang if (RedisModule_StringToLongLong(ele,&val) != REDISMODULE_OK) val = 0;
296*572c4311Sfengbojiang val++;
297*572c4311Sfengbojiang sum += val;
298*572c4311Sfengbojiang RedisModuleString *newele = RedisModule_CreateStringFromLongLong(ctx,val);
299*572c4311Sfengbojiang RedisModule_ListPush(key,REDISMODULE_LIST_HEAD,newele);
300*572c4311Sfengbojiang }
301*572c4311Sfengbojiang RedisModule_ReplyWithLongLong(ctx,sum);
302*572c4311Sfengbojiang RedisModule_ReplicateVerbatim(ctx);
303*572c4311Sfengbojiang return REDISMODULE_OK;
304*572c4311Sfengbojiang }
305*572c4311Sfengbojiang
306*572c4311Sfengbojiang /* This is an example of strings DMA access. Given a key containing a string
307*572c4311Sfengbojiang * it toggles the case of each character from lower to upper case or the
308*572c4311Sfengbojiang * other way around.
309*572c4311Sfengbojiang *
310*572c4311Sfengbojiang * No automatic memory management is used in this example (for the sake
311*572c4311Sfengbojiang * of variety).
312*572c4311Sfengbojiang *
313*572c4311Sfengbojiang * HELLO.TOGGLE.CASE key */
HelloToggleCase_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)314*572c4311Sfengbojiang int HelloToggleCase_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
315*572c4311Sfengbojiang if (argc != 2) return RedisModule_WrongArity(ctx);
316*572c4311Sfengbojiang
317*572c4311Sfengbojiang RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
318*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
319*572c4311Sfengbojiang
320*572c4311Sfengbojiang int keytype = RedisModule_KeyType(key);
321*572c4311Sfengbojiang if (keytype != REDISMODULE_KEYTYPE_STRING &&
322*572c4311Sfengbojiang keytype != REDISMODULE_KEYTYPE_EMPTY)
323*572c4311Sfengbojiang {
324*572c4311Sfengbojiang RedisModule_CloseKey(key);
325*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
326*572c4311Sfengbojiang }
327*572c4311Sfengbojiang
328*572c4311Sfengbojiang if (keytype == REDISMODULE_KEYTYPE_STRING) {
329*572c4311Sfengbojiang size_t len, j;
330*572c4311Sfengbojiang char *s = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);
331*572c4311Sfengbojiang for (j = 0; j < len; j++) {
332*572c4311Sfengbojiang if (isupper(s[j])) {
333*572c4311Sfengbojiang s[j] = tolower(s[j]);
334*572c4311Sfengbojiang } else {
335*572c4311Sfengbojiang s[j] = toupper(s[j]);
336*572c4311Sfengbojiang }
337*572c4311Sfengbojiang }
338*572c4311Sfengbojiang }
339*572c4311Sfengbojiang
340*572c4311Sfengbojiang RedisModule_CloseKey(key);
341*572c4311Sfengbojiang RedisModule_ReplyWithSimpleString(ctx,"OK");
342*572c4311Sfengbojiang RedisModule_ReplicateVerbatim(ctx);
343*572c4311Sfengbojiang return REDISMODULE_OK;
344*572c4311Sfengbojiang }
345*572c4311Sfengbojiang
346*572c4311Sfengbojiang /* HELLO.MORE.EXPIRE key milliseconds.
347*572c4311Sfengbojiang *
348*572c4311Sfengbojiang * If they key has already an associated TTL, extends it by "milliseconds"
349*572c4311Sfengbojiang * milliseconds. Otherwise no operation is performed. */
HelloMoreExpire_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)350*572c4311Sfengbojiang int HelloMoreExpire_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
351*572c4311Sfengbojiang RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
352*572c4311Sfengbojiang if (argc != 3) return RedisModule_WrongArity(ctx);
353*572c4311Sfengbojiang
354*572c4311Sfengbojiang mstime_t addms, expire;
355*572c4311Sfengbojiang
356*572c4311Sfengbojiang if (RedisModule_StringToLongLong(argv[2],&addms) != REDISMODULE_OK)
357*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,"ERR invalid expire time");
358*572c4311Sfengbojiang
359*572c4311Sfengbojiang RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
360*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
361*572c4311Sfengbojiang expire = RedisModule_GetExpire(key);
362*572c4311Sfengbojiang if (expire != REDISMODULE_NO_EXPIRE) {
363*572c4311Sfengbojiang expire += addms;
364*572c4311Sfengbojiang RedisModule_SetExpire(key,expire);
365*572c4311Sfengbojiang }
366*572c4311Sfengbojiang return RedisModule_ReplyWithSimpleString(ctx,"OK");
367*572c4311Sfengbojiang }
368*572c4311Sfengbojiang
369*572c4311Sfengbojiang /* HELLO.ZSUMRANGE key startscore endscore
370*572c4311Sfengbojiang * Return the sum of all the scores elements between startscore and endscore.
371*572c4311Sfengbojiang *
372*572c4311Sfengbojiang * The computation is performed two times, one time from start to end and
373*572c4311Sfengbojiang * another time backward. The two scores, returned as a two element array,
374*572c4311Sfengbojiang * should match.*/
HelloZsumRange_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)375*572c4311Sfengbojiang int HelloZsumRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
376*572c4311Sfengbojiang double score_start, score_end;
377*572c4311Sfengbojiang if (argc != 4) return RedisModule_WrongArity(ctx);
378*572c4311Sfengbojiang
379*572c4311Sfengbojiang if (RedisModule_StringToDouble(argv[2],&score_start) != REDISMODULE_OK ||
380*572c4311Sfengbojiang RedisModule_StringToDouble(argv[3],&score_end) != REDISMODULE_OK)
381*572c4311Sfengbojiang {
382*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,"ERR invalid range");
383*572c4311Sfengbojiang }
384*572c4311Sfengbojiang
385*572c4311Sfengbojiang RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
386*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
387*572c4311Sfengbojiang if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) {
388*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
389*572c4311Sfengbojiang }
390*572c4311Sfengbojiang
391*572c4311Sfengbojiang double scoresum_a = 0;
392*572c4311Sfengbojiang double scoresum_b = 0;
393*572c4311Sfengbojiang
394*572c4311Sfengbojiang RedisModule_ZsetFirstInScoreRange(key,score_start,score_end,0,0);
395*572c4311Sfengbojiang while(!RedisModule_ZsetRangeEndReached(key)) {
396*572c4311Sfengbojiang double score;
397*572c4311Sfengbojiang RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);
398*572c4311Sfengbojiang RedisModule_FreeString(ctx,ele);
399*572c4311Sfengbojiang scoresum_a += score;
400*572c4311Sfengbojiang RedisModule_ZsetRangeNext(key);
401*572c4311Sfengbojiang }
402*572c4311Sfengbojiang RedisModule_ZsetRangeStop(key);
403*572c4311Sfengbojiang
404*572c4311Sfengbojiang RedisModule_ZsetLastInScoreRange(key,score_start,score_end,0,0);
405*572c4311Sfengbojiang while(!RedisModule_ZsetRangeEndReached(key)) {
406*572c4311Sfengbojiang double score;
407*572c4311Sfengbojiang RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);
408*572c4311Sfengbojiang RedisModule_FreeString(ctx,ele);
409*572c4311Sfengbojiang scoresum_b += score;
410*572c4311Sfengbojiang RedisModule_ZsetRangePrev(key);
411*572c4311Sfengbojiang }
412*572c4311Sfengbojiang
413*572c4311Sfengbojiang RedisModule_ZsetRangeStop(key);
414*572c4311Sfengbojiang
415*572c4311Sfengbojiang RedisModule_CloseKey(key);
416*572c4311Sfengbojiang
417*572c4311Sfengbojiang RedisModule_ReplyWithArray(ctx,2);
418*572c4311Sfengbojiang RedisModule_ReplyWithDouble(ctx,scoresum_a);
419*572c4311Sfengbojiang RedisModule_ReplyWithDouble(ctx,scoresum_b);
420*572c4311Sfengbojiang return REDISMODULE_OK;
421*572c4311Sfengbojiang }
422*572c4311Sfengbojiang
423*572c4311Sfengbojiang /* HELLO.LEXRANGE key min_lex max_lex min_age max_age
424*572c4311Sfengbojiang * This command expects a sorted set stored at key in the following form:
425*572c4311Sfengbojiang * - All the elements have score 0.
426*572c4311Sfengbojiang * - Elements are pairs of "<name>:<age>", for example "Anna:52".
427*572c4311Sfengbojiang * The command will return all the sorted set items that are lexicographically
428*572c4311Sfengbojiang * between the specified range (using the same format as ZRANGEBYLEX)
429*572c4311Sfengbojiang * and having an age between min_age and max_age. */
HelloLexRange_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)430*572c4311Sfengbojiang int HelloLexRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
431*572c4311Sfengbojiang RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
432*572c4311Sfengbojiang
433*572c4311Sfengbojiang if (argc != 6) return RedisModule_WrongArity(ctx);
434*572c4311Sfengbojiang
435*572c4311Sfengbojiang RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
436*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
437*572c4311Sfengbojiang if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) {
438*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
439*572c4311Sfengbojiang }
440*572c4311Sfengbojiang
441*572c4311Sfengbojiang if (RedisModule_ZsetFirstInLexRange(key,argv[2],argv[3]) != REDISMODULE_OK) {
442*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,"invalid range");
443*572c4311Sfengbojiang }
444*572c4311Sfengbojiang
445*572c4311Sfengbojiang int arraylen = 0;
446*572c4311Sfengbojiang RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);
447*572c4311Sfengbojiang while(!RedisModule_ZsetRangeEndReached(key)) {
448*572c4311Sfengbojiang double score;
449*572c4311Sfengbojiang RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);
450*572c4311Sfengbojiang RedisModule_ReplyWithString(ctx,ele);
451*572c4311Sfengbojiang RedisModule_FreeString(ctx,ele);
452*572c4311Sfengbojiang RedisModule_ZsetRangeNext(key);
453*572c4311Sfengbojiang arraylen++;
454*572c4311Sfengbojiang }
455*572c4311Sfengbojiang RedisModule_ZsetRangeStop(key);
456*572c4311Sfengbojiang RedisModule_ReplySetArrayLength(ctx,arraylen);
457*572c4311Sfengbojiang RedisModule_CloseKey(key);
458*572c4311Sfengbojiang return REDISMODULE_OK;
459*572c4311Sfengbojiang }
460*572c4311Sfengbojiang
461*572c4311Sfengbojiang /* HELLO.HCOPY key srcfield dstfield
462*572c4311Sfengbojiang * This is just an example command that sets the hash field dstfield to the
463*572c4311Sfengbojiang * same value of srcfield. If srcfield does not exist no operation is
464*572c4311Sfengbojiang * performed.
465*572c4311Sfengbojiang *
466*572c4311Sfengbojiang * The command returns 1 if the copy is performed (srcfield exists) otherwise
467*572c4311Sfengbojiang * 0 is returned. */
HelloHCopy_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)468*572c4311Sfengbojiang int HelloHCopy_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
469*572c4311Sfengbojiang RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
470*572c4311Sfengbojiang
471*572c4311Sfengbojiang if (argc != 4) return RedisModule_WrongArity(ctx);
472*572c4311Sfengbojiang RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
473*572c4311Sfengbojiang REDISMODULE_READ|REDISMODULE_WRITE);
474*572c4311Sfengbojiang int type = RedisModule_KeyType(key);
475*572c4311Sfengbojiang if (type != REDISMODULE_KEYTYPE_HASH &&
476*572c4311Sfengbojiang type != REDISMODULE_KEYTYPE_EMPTY)
477*572c4311Sfengbojiang {
478*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
479*572c4311Sfengbojiang }
480*572c4311Sfengbojiang
481*572c4311Sfengbojiang /* Get the old field value. */
482*572c4311Sfengbojiang RedisModuleString *oldval;
483*572c4311Sfengbojiang RedisModule_HashGet(key,REDISMODULE_HASH_NONE,argv[2],&oldval,NULL);
484*572c4311Sfengbojiang if (oldval) {
485*572c4311Sfengbojiang RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[3],oldval,NULL);
486*572c4311Sfengbojiang }
487*572c4311Sfengbojiang RedisModule_ReplyWithLongLong(ctx,oldval != NULL);
488*572c4311Sfengbojiang return REDISMODULE_OK;
489*572c4311Sfengbojiang }
490*572c4311Sfengbojiang
491*572c4311Sfengbojiang /* HELLO.LEFTPAD str len ch
492*572c4311Sfengbojiang * This is an implementation of the infamous LEFTPAD function, that
493*572c4311Sfengbojiang * was at the center of an issue with the npm modules system in March 2016.
494*572c4311Sfengbojiang *
495*572c4311Sfengbojiang * LEFTPAD is a good example of using a Redis Modules API called
496*572c4311Sfengbojiang * "pool allocator", that was a famous way to allocate memory in yet another
497*572c4311Sfengbojiang * open source project, the Apache web server.
498*572c4311Sfengbojiang *
499*572c4311Sfengbojiang * The concept is very simple: there is memory that is useful to allocate
500*572c4311Sfengbojiang * only in the context of serving a request, and must be freed anyway when
501*572c4311Sfengbojiang * the callback implementing the command returns. So in that case the module
502*572c4311Sfengbojiang * does not need to retain a reference to these allocations, it is just
503*572c4311Sfengbojiang * required to free the memory before returning. When this is the case the
504*572c4311Sfengbojiang * module can call RedisModule_PoolAlloc() instead, that works like malloc()
505*572c4311Sfengbojiang * but will automatically free the memory when the module callback returns.
506*572c4311Sfengbojiang *
507*572c4311Sfengbojiang * Note that PoolAlloc() does not necessarily require AutoMemory to be
508*572c4311Sfengbojiang * active. */
HelloLeftPad_RedisCommand(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)509*572c4311Sfengbojiang int HelloLeftPad_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
510*572c4311Sfengbojiang RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
511*572c4311Sfengbojiang long long padlen;
512*572c4311Sfengbojiang
513*572c4311Sfengbojiang if (argc != 4) return RedisModule_WrongArity(ctx);
514*572c4311Sfengbojiang
515*572c4311Sfengbojiang if ((RedisModule_StringToLongLong(argv[2],&padlen) != REDISMODULE_OK) ||
516*572c4311Sfengbojiang (padlen< 0)) {
517*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,"ERR invalid padding length");
518*572c4311Sfengbojiang }
519*572c4311Sfengbojiang size_t strlen, chlen;
520*572c4311Sfengbojiang const char *str = RedisModule_StringPtrLen(argv[1], &strlen);
521*572c4311Sfengbojiang const char *ch = RedisModule_StringPtrLen(argv[3], &chlen);
522*572c4311Sfengbojiang
523*572c4311Sfengbojiang /* If the string is already larger than the target len, just return
524*572c4311Sfengbojiang * the string itself. */
525*572c4311Sfengbojiang if (strlen >= (size_t)padlen)
526*572c4311Sfengbojiang return RedisModule_ReplyWithString(ctx,argv[1]);
527*572c4311Sfengbojiang
528*572c4311Sfengbojiang /* Padding must be a single character in this simple implementation. */
529*572c4311Sfengbojiang if (chlen != 1)
530*572c4311Sfengbojiang return RedisModule_ReplyWithError(ctx,
531*572c4311Sfengbojiang "ERR padding must be a single char");
532*572c4311Sfengbojiang
533*572c4311Sfengbojiang /* Here we use our pool allocator, for our throw-away allocation. */
534*572c4311Sfengbojiang padlen -= strlen;
535*572c4311Sfengbojiang char *buf = RedisModule_PoolAlloc(ctx,padlen+strlen);
536*572c4311Sfengbojiang for (long long j = 0; j < padlen; j++) buf[j] = *ch;
537*572c4311Sfengbojiang memcpy(buf+padlen,str,strlen);
538*572c4311Sfengbojiang
539*572c4311Sfengbojiang RedisModule_ReplyWithStringBuffer(ctx,buf,padlen+strlen);
540*572c4311Sfengbojiang return REDISMODULE_OK;
541*572c4311Sfengbojiang }
542*572c4311Sfengbojiang
543*572c4311Sfengbojiang /* This function must be present on each Redis module. It is used in order to
544*572c4311Sfengbojiang * register the commands into the Redis server. */
RedisModule_OnLoad(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)545*572c4311Sfengbojiang int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
546*572c4311Sfengbojiang if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1)
547*572c4311Sfengbojiang == REDISMODULE_ERR) return REDISMODULE_ERR;
548*572c4311Sfengbojiang
549*572c4311Sfengbojiang /* Log the list of parameters passing loading the module. */
550*572c4311Sfengbojiang for (int j = 0; j < argc; j++) {
551*572c4311Sfengbojiang const char *s = RedisModule_StringPtrLen(argv[j],NULL);
552*572c4311Sfengbojiang printf("Module loaded with ARGV[%d] = %s\n", j, s);
553*572c4311Sfengbojiang }
554*572c4311Sfengbojiang
555*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.simple",
556*572c4311Sfengbojiang HelloSimple_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR)
557*572c4311Sfengbojiang return REDISMODULE_ERR;
558*572c4311Sfengbojiang
559*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.push.native",
560*572c4311Sfengbojiang HelloPushNative_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
561*572c4311Sfengbojiang return REDISMODULE_ERR;
562*572c4311Sfengbojiang
563*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.push.call",
564*572c4311Sfengbojiang HelloPushCall_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
565*572c4311Sfengbojiang return REDISMODULE_ERR;
566*572c4311Sfengbojiang
567*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.push.call2",
568*572c4311Sfengbojiang HelloPushCall2_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
569*572c4311Sfengbojiang return REDISMODULE_ERR;
570*572c4311Sfengbojiang
571*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.list.sum.len",
572*572c4311Sfengbojiang HelloListSumLen_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
573*572c4311Sfengbojiang return REDISMODULE_ERR;
574*572c4311Sfengbojiang
575*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.list.splice",
576*572c4311Sfengbojiang HelloListSplice_RedisCommand,"write deny-oom",1,2,1) == REDISMODULE_ERR)
577*572c4311Sfengbojiang return REDISMODULE_ERR;
578*572c4311Sfengbojiang
579*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.list.splice.auto",
580*572c4311Sfengbojiang HelloListSpliceAuto_RedisCommand,
581*572c4311Sfengbojiang "write deny-oom",1,2,1) == REDISMODULE_ERR)
582*572c4311Sfengbojiang return REDISMODULE_ERR;
583*572c4311Sfengbojiang
584*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.rand.array",
585*572c4311Sfengbojiang HelloRandArray_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR)
586*572c4311Sfengbojiang return REDISMODULE_ERR;
587*572c4311Sfengbojiang
588*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.repl1",
589*572c4311Sfengbojiang HelloRepl1_RedisCommand,"write",0,0,0) == REDISMODULE_ERR)
590*572c4311Sfengbojiang return REDISMODULE_ERR;
591*572c4311Sfengbojiang
592*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.repl2",
593*572c4311Sfengbojiang HelloRepl2_RedisCommand,"write",1,1,1) == REDISMODULE_ERR)
594*572c4311Sfengbojiang return REDISMODULE_ERR;
595*572c4311Sfengbojiang
596*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.toggle.case",
597*572c4311Sfengbojiang HelloToggleCase_RedisCommand,"write",1,1,1) == REDISMODULE_ERR)
598*572c4311Sfengbojiang return REDISMODULE_ERR;
599*572c4311Sfengbojiang
600*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.more.expire",
601*572c4311Sfengbojiang HelloMoreExpire_RedisCommand,"write",1,1,1) == REDISMODULE_ERR)
602*572c4311Sfengbojiang return REDISMODULE_ERR;
603*572c4311Sfengbojiang
604*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.zsumrange",
605*572c4311Sfengbojiang HelloZsumRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
606*572c4311Sfengbojiang return REDISMODULE_ERR;
607*572c4311Sfengbojiang
608*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.lexrange",
609*572c4311Sfengbojiang HelloLexRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
610*572c4311Sfengbojiang return REDISMODULE_ERR;
611*572c4311Sfengbojiang
612*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.hcopy",
613*572c4311Sfengbojiang HelloHCopy_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
614*572c4311Sfengbojiang return REDISMODULE_ERR;
615*572c4311Sfengbojiang
616*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"hello.leftpad",
617*572c4311Sfengbojiang HelloLeftPad_RedisCommand,"",1,1,1) == REDISMODULE_ERR)
618*572c4311Sfengbojiang return REDISMODULE_ERR;
619*572c4311Sfengbojiang
620*572c4311Sfengbojiang return REDISMODULE_OK;
621*572c4311Sfengbojiang }
622