/* HELLO.LEFTPAD str len ch * This is an implementation of the infamous LEFTPAD function, that * was at the center of an issue with the npm modules system in March 2016. * * LEFTPAD is a good example of using a Redis Modules API called * "pool allocator", that was a famous way to allocate memory in yet another * open source project, the Apache web server. * * The concept is very simple: there is memory that is useful to allocate * only in the context of serving a request, and must be freed anyway when * the callback implementing the command returns. So in that case the module * does not need to retain a reference to these allocations, it is just * required to free the memory before returning. When this is the case the * module can call RedisModule_PoolAlloc() instead, that works like malloc() * but will automatically free the memory when the module callback returns. * * Note that PoolAlloc() does not necessarily require AutoMemory to be * active. */ int HelloLeftPad_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ long long padlen; if (argc != 4) return RedisModule_WrongArity(ctx); if ((RedisModule_StringToLongLong(argv[2],&padlen) != REDISMODULE_OK) || (padlen< 0)) { return RedisModule_ReplyWithError(ctx,"ERR invalid padding length"); } size_t strlen, chlen; const char *str = RedisModule_StringPtrLen(argv[1], &strlen); const char *ch = RedisModule_StringPtrLen(argv[3], &chlen); /* If the string is already larger than the target len, just return * the string itself. */ if (strlen >= padlen) return RedisModule_ReplyWithString(ctx,argv[1]); /* Padding must be a single character in this simple implementation. */ if (chlen != 1) return RedisModule_ReplyWithError(ctx, "ERR padding must be a single char"); /* Here we use our pool allocator, for our throw-away allocation. */ padlen -= strlen; char *buf = RedisModule_PoolAlloc(ctx,padlen+strlen); for (size_t j = 0; j < padlen; j++) buf[j] = *ch; memcpy(buf+padlen,str,strlen); RedisModule_ReplyWithStringBuffer(ctx,buf,padlen+strlen); return REDISMODULE_OK; }
/* HELLOTYPE.INSERT key value */ int HelloTypeInsert_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 3) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != HelloType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } long long value; if ((RedisModule_StringToLongLong(argv[2],&value) != REDISMODULE_OK)) { return RedisModule_ReplyWithError(ctx,"ERR invalid value: must be a signed 64 bit integer"); } /* Create an empty value object if the key is currently empty. */ struct HelloTypeObject *hto; if (type == REDISMODULE_KEYTYPE_EMPTY) { hto = createHelloTypeObject(); RedisModule_ModuleTypeSetValue(key,HelloType,hto); } else { hto = RedisModule_ModuleTypeGetValue(key); } /* Insert the new element. */ HelloTypeInsert(hto,value); RedisModule_ReplyWithLongLong(ctx,hto->len); RedisModule_ReplicateVerbatim(ctx); return REDISMODULE_OK; }
static int UpdateTransformedImage(RedisModuleCtx *ctx, RedisModuleKey *img_key, const char *transformed_img, size_t img_len) { int res = REDISMODULE_ERR; // Resize the key to contain the transformed image buffer if (RedisModule_StringTruncate(img_key, img_len) == REDISMODULE_ERR) { RedisModule_ReplyWithError(ctx, "Error resizing string key"); return res; } // Get write access to the resized key char *buf = RedisModule_StringDMA(img_key, &img_len, REDISMODULE_WRITE); if (!buf) { RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return res; } // Write transformed blob to output memcpy(buf, transformed_img, img_len); // Setup success reply RedisModule_ReplyWithSimpleString(ctx, "OK"); res = REDISMODULE_OK; return res; }
/* HELLOTYPE.RANGE key first count */ int HelloTypeRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 4) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != HelloType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } long long first, count; if (RedisModule_StringToLongLong(argv[2],&first) != REDISMODULE_OK || RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK || first < 0 || count < 0) { return RedisModule_ReplyWithError(ctx, "ERR invalid first or count parameters"); } struct HelloTypeObject *hto = RedisModule_ModuleTypeGetValue(key); struct HelloTypeNode *node = hto ? hto->head : NULL; RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN); long long arraylen = 0; while(node && count--) { RedisModule_ReplyWithLongLong(ctx,node->value); arraylen++; node = node->next; } RedisModule_ReplySetArrayLength(ctx,arraylen); return REDISMODULE_OK; }
static void validate(RedisModuleCtx *ctx, RedisModuleCallReply *reply, RedisModuleString *password) { const char *reply_str; size_t reply_len; const char *pass; size_t pass_len; const char *crypt_pass; size_t crypt_pass_len; char crypt_buf[64]; if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_NULL) { RedisModule_ReplyWithLongLong(ctx, 0); return; } if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_STRING) { RedisModule_ReplyWithError(ctx, "WRONGTYPE Operation against a key holding the wrong kind of value"); return; } reply_str = RedisModule_CallReplyStringPtr(reply, &reply_len); pass = RedisModule_StringPtrLen(password, &pass_len); crypt_pass = crypt_rn(pass, reply_str, crypt_buf, sizeof(crypt_buf)); if (!crypt_pass) { RedisModule_ReplyWithError(ctx, "ERR hash error"); return; } crypt_pass_len = strlen(crypt_pass); if (crypt_pass_len == reply_len && !memcmp(reply_str, crypt_pass, crypt_pass_len)) { RedisModule_ReplyWithLongLong(ctx, 1); } else { RedisModule_ReplyWithLongLong(ctx, 0); } }
/* HELLO.LEXRANGE key min_lex max_lex min_age max_age * This command expects a sorted set stored at key in the following form: * - All the elements have score 0. * - Elements are pairs of "<name>:<age>", for example "Anna:52". * The command will return all the sorted set items that are lexicographically * between the specified range (using the same format as ZRANGEBYLEX) * and having an age between min_age and max_age. */ int HelloLexRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 6) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } if (RedisModule_ZsetFirstInLexRange(key,argv[2],argv[3]) != REDISMODULE_OK) { return RedisModule_ReplyWithError(ctx,"invalid range"); } int arraylen = 0; RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN); while(!RedisModule_ZsetRangeEndReached(key)) { double score; RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score); RedisModule_ReplyWithString(ctx,ele); RedisModule_FreeString(ctx,ele); RedisModule_ZsetRangeNext(key); arraylen++; } RedisModule_ZsetRangeStop(key); RedisModule_ReplySetArrayLength(ctx,arraylen); RedisModule_CloseKey(key); return REDISMODULE_OK; }
int UniquePushIVCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc < 4) { return RedisModule_WrongArity(ctx); } RedisModule_AutoMemory(ctx); // open the key and make sure it is indeed a Hash and not empty RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != UniqueType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } unique *unique; if (type == REDISMODULE_KEYTYPE_EMPTY) { unique = uniqueCreate(); RedisModule_ModuleTypeSetValue(key,UniqueType,unique); } else { unique = RedisModule_ModuleTypeGetValue(key); } sds skey, sval; size_t skeylen, svallen; const char *pkey; unsigned int lvec = argc-3; pkey = RedisModule_StringPtrLen(argv[2], &skeylen); svallen = lvec *8; skey = sdsnewlen(pkey, skeylen); sval = sdsnewlen(NULL, svallen); long long *vec = (long long*)sval; int i; for (i=3; i<argc; i++) { if (REDISMODULE_OK != RedisModule_StringToLongLong(argv[i], vec+i-3)) { return RedisModule_WrongArity(ctx); } } int n = uniquePush(unique, skey, sval, merge_int64); if (n == -1) { RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } else { RedisModule_ReplyWithLongLong(ctx, n); } RedisModule_ReplicateVerbatim(ctx); return REDISMODULE_OK; }
/* ## FT.SUGGADD key string score [INCR] [PAYLOAD {payload}] Add a suggestion string to an auto-complete suggestion dictionary. This is disconnected from the index definitions, and leaves creating and updating suggestino dictionaries to the user. ### Parameters: - key: the suggestion dictionary key. - string: the suggestion string we index - score: a floating point number of the suggestion string's weight -INCR: if set, we increment the existing entry of the suggestion by the given score, instead of replacing the score. This is useful for updating the dictionary based on user queries in real time - PAYLOAD: Add a payload to the suggestion string that will be used as additional information. ### Returns: Integer reply: the current size of the suggestion dictionary. */ int RSSuggestAddCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc < 4 || argc > 7) { return RedisModule_WrongArity(ctx); } int incr = 0, rv = AC_OK; RSPayload payload = {0}; ArgsCursor ac = {0}; ArgsCursor_InitRString(&ac, argv + 4, argc - 4); while (!AC_IsAtEnd(&ac)) { const char *s = AC_GetStringNC(&ac, NULL); if (!strcasecmp(s, "INCR")) { incr = 1; } else if (!strcasecmp(s, "PAYLOAD")) { if ((rv = AC_GetString(&ac, (const char **)&payload.data, &payload.len, 0)) != AC_OK) { return RMUtil_ReplyWithErrorFmt(ctx, "Invalid payload: %s", AC_Strerror(rv)); } } else { return RMUtil_ReplyWithErrorFmt(ctx, "Unknown argument `%s`", s); } } RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != TrieType) { return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); } RedisModuleString *val = argv[2]; double score; if ((RedisModule_StringToDouble(argv[3], &score) != REDISMODULE_OK)) { return RedisModule_ReplyWithError(ctx, "ERR invalid score"); } /* Create an empty value object if the key is currently empty. */ Trie *tree; if (type == REDISMODULE_KEYTYPE_EMPTY) { tree = NewTrie(); RedisModule_ModuleTypeSetValue(key, TrieType, tree); } else { tree = RedisModule_ModuleTypeGetValue(key); } /* Insert the new element. */ Trie_Insert(tree, val, score, incr, &payload); RedisModule_ReplyWithLongLong(ctx, tree->size); RedisModule_ReplicateVerbatim(ctx); return REDISMODULE_OK; }
int GMSwirlCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 3) { return RedisModule_WrongArity(ctx); } // init auto memory for created strings RedisModule_AutoMemory(ctx); // open the key RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ|REDISMODULE_WRITE); // If key doesn't exist then return immediately if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { RedisModule_ReplyWithError(ctx, "empty key"); return REDISMODULE_OK; } // Validate key is a string if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_STRING) { RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return REDISMODULE_ERR; } // Validate input is valid float double degrees; if (RedisModule_StringToDouble(argv[2], °rees) == REDISMODULE_ERR) { RedisModule_ReplyWithError(ctx, "Invalid arguments"); return REDISMODULE_ERR; } // Get access to the image size_t key_len; char *buf = RedisModule_StringDMA(key, &key_len, REDISMODULE_READ); if (!buf) { RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return REDISMODULE_ERR; } struct GoImgSwirl_return r = GoImgSwirl(buf, key_len, degrees); if (r.r3 != 0) { RedisModule_ReplyWithError(ctx, r.r2); free(r.r0); free(r.r2); return REDISMODULE_ERR; } int result = UpdateTransformedImage(ctx, key, r.r0, r.r1); free(r.r0); free(r.r2); return result; }
int UniqueLenCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) { return RedisModule_WrongArity(ctx); } RedisModule_AutoMemory(ctx); // open the key and make sure it is indeed a Hash and not empty RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); int type = RedisModule_KeyType(key); if (type == REDISMODULE_KEYTYPE_EMPTY) { RedisModule_ReplyWithNull(ctx); return REDISMODULE_OK; } if (RedisModule_ModuleTypeGetType(key) != UniqueType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } unique *unique = RedisModule_ModuleTypeGetValue(key); size_t len = uniqueLen(unique); RedisModule_ReplyWithLongLong(ctx, len); return REDISMODULE_OK; }
int UniqueGetAllCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) { return RedisModule_WrongArity(ctx); } RedisModule_AutoMemory(ctx); // open the key and make sure it is indeed a Hash and not empty RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); int type = RedisModule_KeyType(key); if (type == REDISMODULE_KEYTYPE_EMPTY) { RedisModule_ReplyWithNull(ctx); return REDISMODULE_OK; } if (RedisModule_ModuleTypeGetType(key) != UniqueType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } unique *unique = RedisModule_ModuleTypeGetValue(key); size_t len = uniqueLen(unique); RedisModule_ReplyWithArray(ctx, len*2); size_t i; listIter *it = listGetIterator(unique->l, AL_START_HEAD); listNode *node; for (node = listNext(it), i=0; node && i<len; node = listNext(it),i++) { dictEntry *en = node->value; RedisModule_ReplyWithStringBuffer(ctx, en->key, sdslen(en->key)); RedisModule_ReplyWithStringBuffer(ctx, en->v.val, sdslen(en->v.val)); } return REDISMODULE_OK; }
/* Another command to show replication. In this case, we call * RedisModule_ReplicateVerbatim() to mean we want just the command to be * propagated to slaves / AOF exactly as it was called by the user. * * This command also shows how to work with string objects. * It takes a list, and increments all the elements (that must have * a numerical value) by 1, returning the sum of all the elements * as reply. * * Usage: HELLO.REPL2 <list-key> */ int HelloRepl2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) return RedisModule_WrongArity(ctx); RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST) return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); size_t listlen = RedisModule_ValueLength(key); long long sum = 0; /* Rotate and increment. */ while(listlen--) { RedisModuleString *ele = RedisModule_ListPop(key,REDISMODULE_LIST_TAIL); long long val; if (RedisModule_StringToLongLong(ele,&val) != REDISMODULE_OK) val = 0; val++; sum += val; RedisModuleString *newele = RedisModule_CreateStringFromLongLong(ctx,val); RedisModule_ListPush(key,REDISMODULE_LIST_HEAD,newele); } RedisModule_ReplyWithLongLong(ctx,sum); RedisModule_ReplicateVerbatim(ctx); return REDISMODULE_OK; }
int UniquePopCommandHelper(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, PopReplyCB replycb) { if (argc != 2) { return RedisModule_WrongArity(ctx); } RedisModule_AutoMemory(ctx); // open the key and make sure it is indeed a Hash and not empty RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); int type = RedisModule_KeyType(key); if (type == REDISMODULE_KEYTYPE_EMPTY) { RedisModule_ReplyWithNull(ctx); return REDISMODULE_OK; } if (RedisModule_ModuleTypeGetType(key) != UniqueType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } unique *unique = RedisModule_ModuleTypeGetValue(key); sds skey, sval; int r = uniquePop(unique, &skey, &sval); if (r == 0) { replycb(ctx, skey, sval); sdsfree(skey); sdsfree(sval); } else { RedisModule_ReplyWithNull(ctx); } return REDISMODULE_OK; }
/* This is an example of strings DMA access. Given a key containing a string * it toggles the case of each character from lower to upper case or the * other way around. * * No automatic memory management is used in this example (for the sake * of variety). * * HELLO.TOGGLE.CASE key */ int HelloToggleCase_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); int keytype = RedisModule_KeyType(key); if (keytype != REDISMODULE_KEYTYPE_STRING && keytype != REDISMODULE_KEYTYPE_EMPTY) { RedisModule_CloseKey(key); return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } if (keytype == REDISMODULE_KEYTYPE_STRING) { size_t len, j; char *s = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE); for (j = 0; j < len; j++) { if (isupper(s[j])) { s[j] = tolower(s[j]); } else { s[j] = toupper(s[j]); } } } RedisModule_CloseKey(key); RedisModule_ReplyWithSimpleString(ctx,"OK"); RedisModule_ReplicateVerbatim(ctx); return REDISMODULE_OK; }
/* HELLO.ZSUMRANGE key startscore endscore * Return the sum of all the scores elements between startscore and endscore. * * The computation is performed two times, one time from start to end and * another time backward. The two scores, returned as a two element array, * should match.*/ int HelloZsumRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { double score_start, score_end; if (argc != 4) return RedisModule_WrongArity(ctx); if (RedisModule_StringToDouble(argv[2],&score_start) != REDISMODULE_OK || RedisModule_StringToDouble(argv[3],&score_end) != REDISMODULE_OK) { return RedisModule_ReplyWithError(ctx,"ERR invalid range"); } RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } double scoresum_a = 0; double scoresum_b = 0; RedisModule_ZsetFirstInScoreRange(key,score_start,score_end,0,0); while(!RedisModule_ZsetRangeEndReached(key)) { double score; RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score); RedisModule_FreeString(ctx,ele); scoresum_a += score; RedisModule_ZsetRangeNext(key); } RedisModule_ZsetRangeStop(key); RedisModule_ZsetLastInScoreRange(key,score_start,score_end,0,0); while(!RedisModule_ZsetRangeEndReached(key)) { double score; RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score); RedisModule_FreeString(ctx,ele); scoresum_b += score; RedisModule_ZsetRangePrev(key); } RedisModule_ZsetRangeStop(key); RedisModule_CloseKey(key); RedisModule_ReplyWithArray(ctx,2); RedisModule_ReplyWithDouble(ctx,scoresum_a); RedisModule_ReplyWithDouble(ctx,scoresum_b); return REDISMODULE_OK; }
int GMGetTypeCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { int res = REDISMODULE_ERR; if (argc != 2) { return RedisModule_WrongArity(ctx); } // init auto memory for created strings RedisModule_AutoMemory(ctx); // open the key RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); // If key doesn't exist then return immediately if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { RedisModule_ReplyWithError(ctx, "empty key"); return REDISMODULE_OK; } // Validate key is a string if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_STRING) { RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return REDISMODULE_ERR; } // Get access to the image size_t key_len; char *buf = RedisModule_StringDMA(key, &key_len, REDISMODULE_READ); if (!buf) { RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return REDISMODULE_ERR; } struct GoGetImgType_return r = GoGetImgType(buf, key_len); if (r.r3 != 0) { RedisModule_ReplyWithError(ctx, r.r2); free(r.r0); free(r.r2); return REDISMODULE_ERR; } RedisModule_ReplyWithSimpleString(ctx, r.r0); free(r.r0); free(r.r2); return REDISMODULE_OK; }
static int UniquePushCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, mergefn fn) { if (argc != 4) { return RedisModule_WrongArity(ctx); } RedisModule_AutoMemory(ctx); // open the key and make sure it is indeed a Hash and not empty RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != UniqueType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } unique *unique; if (type == REDISMODULE_KEYTYPE_EMPTY) { unique = uniqueCreate(); RedisModule_ModuleTypeSetValue(key,UniqueType,unique); } else { unique = RedisModule_ModuleTypeGetValue(key); } sds skey, sval; size_t skeylen, svallen; const char *pkey, *pval; pkey = RedisModule_StringPtrLen(argv[2], &skeylen); pval = RedisModule_StringPtrLen(argv[3], &svallen); skey = sdsnewlen(pkey, skeylen); sval = sdsnewlen(pval, svallen); int n = uniquePush(unique, skey, sval, fn); if (n == -1) { RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } else { RedisModule_ReplyWithLongLong(ctx, n); } RedisModule_ReplicateVerbatim(ctx); return REDISMODULE_OK; }
/* HELLO.LIST.SPLICE srclist dstlist count * Moves 'count' elements from the tail of 'srclist' to the head of * 'dstlist'. If less than count elements are available, it moves as much * elements as possible. */ int HelloListSplice_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 4) return RedisModule_WrongArity(ctx); RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2], REDISMODULE_READ|REDISMODULE_WRITE); /* Src and dst key must be empty or lists. */ if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST && RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) || (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST && RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY)) { RedisModule_CloseKey(srckey); RedisModule_CloseKey(dstkey); return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } long long count; if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) || (count < 0)) { RedisModule_CloseKey(srckey); RedisModule_CloseKey(dstkey); return RedisModule_ReplyWithError(ctx,"ERR invalid count"); } while(count-- > 0) { RedisModuleString *ele; ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL); if (ele == NULL) break; RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele); RedisModule_FreeString(ctx,ele); } size_t len = RedisModule_ValueLength(srckey); RedisModule_CloseKey(srckey); RedisModule_CloseKey(dstkey); RedisModule_ReplyWithLongLong(ctx,len); return REDISMODULE_OK; }
static bool SpellCheck_CheckDictExistence(SpellCheckCtx *scCtx, const char *dict) { #define BUFF_SIZE 1000 RedisModuleKey *k = NULL; Trie *t = SpellCheck_OpenDict(scCtx->sctx->redisCtx, dict, REDISMODULE_READ, &k); if (t == NULL) { char buff[BUFF_SIZE]; snprintf(buff, BUFF_SIZE, "Dict does not exist: %s", dict); RedisModule_ReplyWithError(scCtx->sctx->redisCtx, buff); return false; } RedisModule_CloseKey(k); return true; }
/* ## FT.SUGLEN key Get the size of an autoc-complete suggestion dictionary ### Parameters: - key: the suggestion dictionary key. ### Returns: Integer reply: the current size of the suggestion dictionary. */ int RSSuggestLenCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 2) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != TrieType) { return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); } Trie *tree = RedisModule_ModuleTypeGetValue(key); return RedisModule_ReplyWithLongLong(ctx, tree ? tree->size : 0); }
/* HELLO.RAND.ARRAY <count> * Shows how to generate arrays as commands replies. * It just outputs <count> random numbers. */ int HelloRandArray_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) return RedisModule_WrongArity(ctx); long long count; if (RedisModule_StringToLongLong(argv[1],&count) != REDISMODULE_OK || count < 0) return RedisModule_ReplyWithError(ctx,"ERR invalid count"); /* To reply with an array, we call RedisModule_ReplyWithArray() followed * by other "count" calls to other reply functions in order to generate * the elements of the array. */ RedisModule_ReplyWithArray(ctx,count); while(count--) RedisModule_ReplyWithLongLong(ctx,rand()); return REDISMODULE_OK; }
int TestModule(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* TODO: calling flushall but checking only for db 0. */ RedisModuleCallReply *r = RedisModule_Call(ctx, "DBSIZE", ""); if (RedisModule_CallReplyInteger(r) != 0) { RedisModule_ReplyWithError(ctx, "ERR test must be run on an empty instance"); return REDISMODULE_ERR; } RMUtil_Test(testMSIsMember); RedisModule_ReplyWithSimpleString(ctx, "PASS"); return REDISMODULE_OK; }
static int router_simple_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); if (argc < 2) { RedisModule_WrongArity(ctx); return REDISMODULE_OK; } RedisModuleCallReply *reply; size_t len; const char *realCmd = RedisModule_StringPtrLen(argv[1], &len); if (argc == 2) { reply = RedisModule_Call(ctx, realCmd, ""); } else { char *fmt = (char *) malloc(argc-1); memset(fmt, 's', argc-2); fmt[argc-2] = '\0'; ffi_cif cif; ffi_type **ffi_argv = (ffi_type **)malloc(sizeof(ffi_type *) * (argc + 1)); void *result; int i; for (i = 0; i < argc+1; i++) { ffi_argv[i] = &ffi_type_pointer; } void **values = (void **) malloc(sizeof(void *) * (argc + 1)); values[0] = &ctx; values[1] = &realCmd; values[2] = &fmt; for (i = 3; i < argc + 1; i++) { values[i] = &argv[i-1]; } if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, (argc + 1), &ffi_type_pointer, ffi_argv) == FFI_OK) { ffi_call(&cif, FFI_FN(RedisModule_Call), &result, values); } reply = (RedisModuleCallReply *) result; } if (reply == NULL) { const char *err = "command error"; RedisModule_ReplyWithError(ctx, err); } else { RedisModule_ReplyWithCallReply(ctx, reply); } return REDISMODULE_OK; }
int GMThumbnailCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 4) { return RedisModule_WrongArity(ctx); } // init auto memory for created strings RedisModule_AutoMemory(ctx); // open the key RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); // If key doesn't exist then return immediately if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { RedisModule_ReplyWithError(ctx, "empty key"); return REDISMODULE_OK; } // Validate key is a string if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_STRING) { RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return REDISMODULE_ERR; } // Validate inputs are valid unsigned ints long long width; if (RedisModule_StringToLongLong(argv[2], &width) == REDISMODULE_ERR || width <= 0) { RedisModule_ReplyWithError(ctx, "Invalid arguments"); return REDISMODULE_ERR; } long long height; if (RedisModule_StringToLongLong(argv[3], &height) == REDISMODULE_ERR || height <= 0) { RedisModule_ReplyWithError(ctx, "Invalid arguments"); return REDISMODULE_ERR; } // Get access to the image size_t key_len; char *buf = RedisModule_StringDMA(key, &key_len, REDISMODULE_READ); if (!buf) { RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return REDISMODULE_ERR; } struct GoImgThumbnail_return r = GoImgThumbnail(buf, key_len, width, height); if (r.r3 != 0) { RedisModule_ReplyWithError(ctx, r.r2); free(r.r0); free(r.r2); return REDISMODULE_ERR; } int result = UpdateTransformedImage(ctx, key, r.r0, r.r1); free(r.r0); free(r.r2); return result; }
/* HELLOTYPE.LEN key */ int HelloTypeLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 2) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != HelloType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } struct HelloTypeObject *hto = RedisModule_ModuleTypeGetValue(key); RedisModule_ReplyWithLongLong(ctx,hto ? hto->len : 0); return REDISMODULE_OK; }
/* HELLO.MORE.EXPIRE key milliseconds. * * If they key has already an associated TTL, extends it by "milliseconds" * milliseconds. Otherwise no operation is performed. */ int HelloMoreExpire_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 3) return RedisModule_WrongArity(ctx); mstime_t addms, expire; if (RedisModule_StringToLongLong(argv[2],&addms) != REDISMODULE_OK) return RedisModule_ReplyWithError(ctx,"ERR invalid expire time"); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); expire = RedisModule_GetExpire(key); if (expire != REDISMODULE_NO_EXPIRE) { expire += addms; RedisModule_SetExpire(key,expire); } return RedisModule_ReplyWithSimpleString(ctx,"OK"); }
/* ## FT.SUGDEL key str Delete a string from a suggestion index. ### Parameters: - key: the suggestion dictionary key. - str: the string to delete ### Returns: Integer reply: 1 if the string was found and deleted, 0 otherwise. */ int RSSuggestDelCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 3) return RedisModule_WrongArity(ctx); RedisModule_ReplicateVerbatim(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != TrieType) { return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); } Trie *tree = RedisModule_ModuleTypeGetValue(key); if (!tree) { return RedisModule_ReplyWithLongLong(ctx, 0); } size_t len; const char *str = RedisModule_StringPtrLen(argv[2], &len); return RedisModule_ReplyWithLongLong(ctx, Trie_Delete(tree, str, len)); }
/* * MSISMEMBER key1 [key2 ...] member * Checks for membership in multiple sets. * Reply: Integer, the count of sets to which the member belongs. */ int MSIsMemberCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc < 3) { if (RedisModule_IsKeysPositionRequest(ctx)) /* TODO: handle this once the getkey-api allows signalling errors */ return REDISMODULE_OK; else return RedisModule_WrongArity(ctx); } if (RedisModule_IsKeysPositionRequest(ctx)) { size_t i; for (i = 1; i < argc - 1; i++) RedisModule_KeyAtPos(ctx, i); return REDISMODULE_OK; } RedisModule_AutoMemory(ctx); int iele = argc - 1; size_t count = 0; int i; for (i = 1; i < iele; i++) { RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[i], REDISMODULE_READ | REDISMODULE_WRITE); if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) continue; if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_SET) { RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return REDISMODULE_ERR; } RedisModuleCallReply *rep = RedisModule_Call(ctx, "SISMEMBER", "ss", argv[i], argv[iele]); RMUTIL_ASSERT_NOERROR(rep) count += RedisModule_CallReplyInteger(rep); } RedisModule_ReplyWithLongLong(ctx, count); return REDISMODULE_OK; }
static int cmd_password_hset(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModuleCallReply *reply; size_t len; char crypt_buf[64]; char *hash; if (argc != 4) { RedisModule_WrongArity(ctx); return REDISMODULE_OK; } hash = do_crypt(RedisModule_StringPtrLen(argv[3], &len), crypt_buf, sizeof(crypt_buf)); if (!hash) { RedisModule_ReplyWithError(ctx, "ERR hash error"); return REDISMODULE_ERR; } reply = RedisModule_Call(ctx, "HSET", "ssc!", argv[1], argv[2], hash); RedisModule_ReplyWithCallReply(ctx, reply); return REDISMODULE_OK; }
/* HELLO.HCOPY key srcfield dstfield * This is just an example command that sets the hash field dstfield to the * same value of srcfield. If srcfield does not exist no operation is * performed. * * The command returns 1 if the copy is performed (srcfield exists) otherwise * 0 is returned. */ int HelloHCopy_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 4) return RedisModule_WrongArity(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], REDISMODULE_READ|REDISMODULE_WRITE); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_HASH && type != REDISMODULE_KEYTYPE_EMPTY) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } /* Get the old field value. */ RedisModuleString *oldval; RedisModule_HashGet(key,REDISMODULE_HASH_NONE,argv[2],&oldval,NULL); if (oldval) { RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[3],oldval,NULL); } RedisModule_ReplyWithLongLong(ctx,oldval != NULL); return REDISMODULE_OK; }