mirror of
https://github.com/fluencelabs/redis
synced 2025-06-17 03:01:20 +00:00
Modules: command <-> core interface modified to get flags & keys.
This commit is contained in:
140
src/module.c
140
src/module.c
@ -52,12 +52,17 @@ struct RedisModuleCtx {
|
||||
int flags; /* REDISMODULE_CTX_... flags. */
|
||||
void **postponed_arrays; /* To set with RM_ReplySetArrayLength(). */
|
||||
int postponed_arrays_count; /* Number of entries in postponed_arrays. */
|
||||
|
||||
/* Used if there is the REDISMODULE_CTX_KEYS_POS_REQUEST flag set. */
|
||||
int *keys_pos;
|
||||
int keys_count;
|
||||
};
|
||||
typedef struct RedisModuleCtx RedisModuleCtx;
|
||||
|
||||
#define REDISMODULE_CTX_INIT {(void*)(unsigned long)&RM_GetApi, NULL, NULL, NULL, 0, 0, 0, NULL, 0}
|
||||
#define REDISMODULE_CTX_INIT {(void*)(unsigned long)&RM_GetApi, NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL, 0}
|
||||
#define REDISMODULE_CTX_MULTI_EMITTED (1<<0)
|
||||
#define REDISMODULE_CTX_AUTO_MEMORY (1<<1)
|
||||
#define REDISMODULE_CTX_KEYS_POS_REQUEST (1<<2)
|
||||
|
||||
/* This represents a Redis key opened with RM_OpenKey(). */
|
||||
struct RedisModuleKey {
|
||||
@ -270,10 +275,92 @@ void RedisModuleCommandDispatcher(client *c) {
|
||||
moduleFreeContext(&ctx);
|
||||
}
|
||||
|
||||
/* This function returns the list of keys, with the same interface as the
|
||||
* 'getkeys' function of the native commands, for module commands that exported
|
||||
* the "getkeys-api" flag during the registration. This is done when the
|
||||
* list of keys are not at fixed positions, so that first/last/step cannot
|
||||
* be used.
|
||||
*
|
||||
* In order to accomplish its work, the module command is called, flagging
|
||||
* the context in a way that the command can recognize this is a special
|
||||
* "get keys" call by calling RedisModule_IsKeysPositionRequest(ctx). */
|
||||
int *moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
RedisModuleCommandProxy *cp = (void*)(unsigned long)cmd->getkeys_proc;
|
||||
RedisModuleCtx ctx = REDISMODULE_CTX_INIT;
|
||||
|
||||
ctx.module = cp->module;
|
||||
ctx.client = NULL;
|
||||
ctx.flags |= REDISMODULE_CTX_KEYS_POS_REQUEST;
|
||||
cp->func(&ctx,(void**)argv,argc);
|
||||
int *res = ctx.keys_pos;
|
||||
if (numkeys) *numkeys = ctx.keys_count;
|
||||
moduleFreeContext(&ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Return non-zero if a module command, that was declared with the
|
||||
* flag "getkeys-api", is called in a special way to get the keys positions
|
||||
* and not to get executed. Otherwise zero is returned. */
|
||||
int RM_IsKeysPositionRequest(RedisModuleCtx *ctx) {
|
||||
return (ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST) != 0;
|
||||
}
|
||||
|
||||
/* When a module command is called in order to obtain the position of
|
||||
* keys, since it was flagged as "getkeys-api" during the registration,
|
||||
* the command implementation checks for this special call using the
|
||||
* RedisModule_IsKeysPositionRequest() API and uses this function in
|
||||
* order to report keys, like in the following example:
|
||||
*
|
||||
* if (RedisModule_IsKeysPositionRequest(ctx)) {
|
||||
* RedisModule_KeyAtPos(ctx,1);
|
||||
* RedisModule_KeyAtPos(ctx,2);
|
||||
* }
|
||||
*
|
||||
* Note: in the example below the get keys API would not be needed since
|
||||
* keys are at fixed positions. This interface is only used for commands
|
||||
* with a more complex structure. */
|
||||
void RM_KeyAtPos(RedisModuleCtx *ctx, int pos) {
|
||||
if (!(ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST)) return;
|
||||
if (pos <= 0) return;
|
||||
ctx->keys_pos = zrealloc(ctx->keys_pos,sizeof(int)*(ctx->keys_count+1));
|
||||
ctx->keys_pos[ctx->keys_count++] = pos;
|
||||
}
|
||||
|
||||
/* Helper for RM_CreateCommand(). Truns a string representing command
|
||||
* flags into the command flags used by the Redis core.
|
||||
*
|
||||
* It returns the set of flags, or -1 if unknown flags are found. */
|
||||
int commandFlagsFromString(char *s) {
|
||||
int count, j;
|
||||
int flags = 0;
|
||||
sds *tokens = sdssplitlen(s,strlen(s)," ",1,&count);
|
||||
for (j = 0; j < count; j++) {
|
||||
char *t = tokens[j];
|
||||
if (!strcasecmp(t,"write")) flags |= CMD_WRITE;
|
||||
else if (!strcasecmp(t,"readonly")) flags |= CMD_READONLY;
|
||||
else if (!strcasecmp(t,"admin")) flags |= CMD_ADMIN;
|
||||
else if (!strcasecmp(t,"deny-oom")) flags |= CMD_DENYOOM;
|
||||
else if (!strcasecmp(t,"deny-script")) flags |= CMD_NOSCRIPT;
|
||||
else if (!strcasecmp(t,"allow-loading")) flags |= CMD_LOADING;
|
||||
else if (!strcasecmp(t,"pubsub")) flags |= CMD_PUBSUB;
|
||||
else if (!strcasecmp(t,"random")) flags |= CMD_RANDOM;
|
||||
else if (!strcasecmp(t,"allow-stale")) flags |= CMD_STALE;
|
||||
else if (!strcasecmp(t,"no-monitor")) flags |= CMD_SKIP_MONITOR;
|
||||
else if (!strcasecmp(t,"fast")) flags |= CMD_FAST;
|
||||
else if (!strcasecmp(t,"getkeys-api")) flags |= CMD_MODULE_GETKEYS;
|
||||
else if (!strcasecmp(t,"no-cluster")) flags |= CMD_MODULE_NO_CLUSTER;
|
||||
else break;
|
||||
}
|
||||
sdsfreesplitres(tokens,count);
|
||||
if (j != count) return -1; /* Some token not processed correctly. */
|
||||
return flags;
|
||||
}
|
||||
|
||||
/* Register a new command in the Redis server, that will be handled by
|
||||
* calling the function pointer 'func' using the RedisModule calling
|
||||
* convention. The function returns REDISMODULE_ERR if the specified command
|
||||
* name is already busy, otherwise REDISMODULE_OK is returned.
|
||||
* name is already busy or a set of invalid flags were passed, otherwise
|
||||
* REDISMODULE_OK is returned and the new command is registered.
|
||||
*
|
||||
* This function must be called during the initialization of the module
|
||||
* inside the RedisModule_OnLoad() function. Calling this function outside
|
||||
@ -284,8 +371,45 @@ void RedisModuleCommandDispatcher(client *c) {
|
||||
* int MyCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
|
||||
*
|
||||
* And is supposed to always return REDISMODULE_OK.
|
||||
*
|
||||
* The set of flags 'strflags' specify the behavior of the command, and should
|
||||
* be passed as a C string compoesd of space separated words, like for
|
||||
* example "write deny-oom". The set of flags are:
|
||||
*
|
||||
* "write": The command may modify the data set (it may also read from it).
|
||||
* "readonly": The command returns data from keys but never writes.
|
||||
* "admin": The command is an administrative command (may change replication
|
||||
* or perform similar tasks).
|
||||
* "deny-oom": The command may use additional memory and should be denied during
|
||||
* out of memory conditions.
|
||||
* "deny-script": Don't allow this command in Lua scripts.
|
||||
* "allow-loading": Allow this command while the server is loading data. Only
|
||||
* commands not interacting with the data set should be allowed
|
||||
* to run in this mode. If not sure don't use this flag.
|
||||
* "pubsub": The command publishes things on Pub/Sub channels.
|
||||
* "random": The command may have different outputs even starting from the
|
||||
* same input arguments and key values.
|
||||
* "allow-stale": The command is allowed to run on slaves that don't serve stale
|
||||
* data. Don't use if you don't know what this means.
|
||||
* "no-monitor": Don't propoagate the command on monitor. Use this if the command
|
||||
* has sensible data among the arguments.
|
||||
* "fast": The command time complexity is not greater than O(log(N)) where
|
||||
* N is the size of the collection or anything else representing
|
||||
* the normal scalability issue with the command.
|
||||
* "getkeys-api": The command implements the interface to return the arguments
|
||||
* that are keys. Used when start/stop/step is not enough because
|
||||
* of the command syntax.
|
||||
* "no-cluster": The command should not register in Redis Cluster since is not
|
||||
* designed to work with it because, for example, is unable to
|
||||
* report the position of the keys, programmatically creates key
|
||||
* names, or any other reason.
|
||||
*/
|
||||
int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc) {
|
||||
int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) {
|
||||
int flags = strflags ? commandFlagsFromString((char*)strflags) : 0;
|
||||
if (flags == -1) return REDISMODULE_ERR;
|
||||
if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
struct redisCommand *rediscmd;
|
||||
RedisModuleCommandProxy *cp;
|
||||
sds cmdname = sdsnew(name);
|
||||
@ -310,11 +434,11 @@ int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc c
|
||||
cp->rediscmd->name = cmdname;
|
||||
cp->rediscmd->proc = RedisModuleCommandDispatcher;
|
||||
cp->rediscmd->arity = -1;
|
||||
cp->rediscmd->flags = 0;
|
||||
cp->rediscmd->flags = flags | CMD_MODULE;
|
||||
cp->rediscmd->getkeys_proc = (redisGetKeysProc*)(unsigned long)cp;
|
||||
cp->rediscmd->firstkey = 1;
|
||||
cp->rediscmd->lastkey = 1;
|
||||
cp->rediscmd->keystep = 1;
|
||||
cp->rediscmd->firstkey = firstkey;
|
||||
cp->rediscmd->lastkey = lastkey;
|
||||
cp->rediscmd->keystep = keystep;
|
||||
cp->rediscmd->microseconds = 0;
|
||||
cp->rediscmd->calls = 0;
|
||||
dictAdd(server.commands,sdsdup(cmdname),cp->rediscmd);
|
||||
@ -2129,6 +2253,8 @@ void moduleRegisterCoreAPI(void) {
|
||||
REGISTER_API(ZsetRangeEndReached);
|
||||
REGISTER_API(HashSet);
|
||||
REGISTER_API(HashGet);
|
||||
REGISTER_API(IsKeysPositionRequest);
|
||||
REGISTER_API(KeyAtPos);
|
||||
}
|
||||
|
||||
/* Global initialization at Redis startup. */
|
||||
|
Reference in New Issue
Block a user