1 /* Hellodict -- An example of modules dictionary API 2 * 3 * This module implements a volatile key-value store on top of the 4 * dictionary exported by the Redis modules API. 5 * 6 * ----------------------------------------------------------------------------- 7 * 8 * Copyright (c) 2018, Salvatore Sanfilippo <antirez at gmail dot com> 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions are met: 13 * 14 * * Redistributions of source code must retain the above copyright notice, 15 * this list of conditions and the following disclaimer. 16 * * Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * * Neither the name of Redis nor the names of its contributors may be used 20 * to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 #define REDISMODULE_EXPERIMENTAL_API 37 #include "../redismodule.h" 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <ctype.h> 41 #include <string.h> 42 43 static RedisModuleDict *Keyspace; 44 45 /* HELLODICT.SET <key> <value> 46 * 47 * Set the specified key to the specified value. */ 48 int cmd_SET(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 49 if (argc != 3) return RedisModule_WrongArity(ctx); 50 RedisModule_DictSet(Keyspace,argv[1],argv[2]); 51 /* We need to keep a reference to the value stored at the key, otherwise 52 * it would be freed when this callback returns. */ 53 RedisModule_RetainString(NULL,argv[2]); 54 return RedisModule_ReplyWithSimpleString(ctx, "OK"); 55 } 56 57 /* HELLODICT.GET <key> 58 * 59 * Return the value of the specified key, or a null reply if the key 60 * is not defined. */ 61 int cmd_GET(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 62 if (argc != 2) return RedisModule_WrongArity(ctx); 63 RedisModuleString *val = RedisModule_DictGet(Keyspace,argv[1],NULL); 64 if (val == NULL) { 65 return RedisModule_ReplyWithNull(ctx); 66 } else { 67 return RedisModule_ReplyWithString(ctx, val); 68 } 69 } 70 71 /* HELLODICT.KEYRANGE <startkey> <endkey> <count> 72 * 73 * Return a list of matching keys, lexicographically between startkey 74 * and endkey. No more than 'count' items are emitted. */ 75 int cmd_KEYRANGE(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 76 if (argc != 4) return RedisModule_WrongArity(ctx); 77 78 /* Parse the count argument. */ 79 long long count; 80 if (RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) { 81 return RedisModule_ReplyWithError(ctx,"ERR invalid count"); 82 } 83 84 /* Seek the iterator. */ 85 RedisModuleDictIter *iter = RedisModule_DictIteratorStart( 86 Keyspace, ">=", argv[1]); 87 88 /* Reply with the matching items. */ 89 char *key; 90 size_t keylen; 91 long long replylen = 0; /* Keep track of the amitted array len. */ 92 RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN); 93 while((key = RedisModule_DictNextC(iter,&keylen,NULL)) != NULL) { 94 if (replylen >= count) break; 95 if (RedisModule_DictCompare(iter,"<=",argv[2]) == REDISMODULE_ERR) 96 break; 97 RedisModule_ReplyWithStringBuffer(ctx,key,keylen); 98 replylen++; 99 } 100 RedisModule_ReplySetArrayLength(ctx,replylen); 101 102 /* Cleanup. */ 103 RedisModule_DictIteratorStop(iter); 104 return REDISMODULE_OK; 105 } 106 107 /* This function must be present on each Redis module. It is used in order to 108 * register the commands into the Redis server. */ 109 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 110 REDISMODULE_NOT_USED(argv); 111 REDISMODULE_NOT_USED(argc); 112 113 if (RedisModule_Init(ctx,"hellodict",1,REDISMODULE_APIVER_1) 114 == REDISMODULE_ERR) return REDISMODULE_ERR; 115 116 if (RedisModule_CreateCommand(ctx,"hellodict.set", 117 cmd_SET,"write deny-oom",1,1,0) == REDISMODULE_ERR) 118 return REDISMODULE_ERR; 119 120 if (RedisModule_CreateCommand(ctx,"hellodict.get", 121 cmd_GET,"readonly",1,1,0) == REDISMODULE_ERR) 122 return REDISMODULE_ERR; 123 124 if (RedisModule_CreateCommand(ctx,"hellodict.keyrange", 125 cmd_KEYRANGE,"readonly",1,1,0) == REDISMODULE_ERR) 126 return REDISMODULE_ERR; 127 128 /* Create our global dictionray. Here we'll set our keys and values. */ 129 Keyspace = RedisModule_CreateDict(NULL); 130 131 return REDISMODULE_OK; 132 } 133