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