Introduce getKeysResult for getKeysFromCommand.
Avoid using a static buffer for short key index responses, and make it caller's responsibility to stack-allocate a result type. Responses that don't fit are still allocated on the heap.
This commit is contained in:
parent
e66e79aa9f
commit
bf5beab64a
@ -1115,8 +1115,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
|
||||
if (!(c->user->flags & USER_FLAG_ALLKEYS) &&
|
||||
(c->cmd->getkeys_proc || c->cmd->firstkey))
|
||||
{
|
||||
int numkeys;
|
||||
int *keyidx = getKeysFromCommand(c->cmd,c->argv,c->argc,&numkeys);
|
||||
getKeysResult result = GETKEYS_RESULT_INIT;
|
||||
int numkeys = getKeysFromCommand(c->cmd,c->argv,c->argc,&result);
|
||||
int *keyidx = result.keys;
|
||||
for (int j = 0; j < numkeys; j++) {
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
@ -1137,11 +1138,11 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
|
||||
}
|
||||
if (!match) {
|
||||
if (keyidxptr) *keyidxptr = keyidx[j];
|
||||
getKeysFreeResult(keyidx);
|
||||
getKeysFreeResult(&result);
|
||||
return ACL_DENIED_KEY;
|
||||
}
|
||||
}
|
||||
getKeysFreeResult(keyidx);
|
||||
getKeysFreeResult(&result);
|
||||
}
|
||||
|
||||
/* If we survived all the above checks, the user can execute the
|
||||
|
@ -5644,7 +5644,10 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
|
||||
margc = ms->commands[i].argc;
|
||||
margv = ms->commands[i].argv;
|
||||
|
||||
keyindex = getKeysFromCommand(mcmd,margv,margc,&numkeys);
|
||||
getKeysResult result = GETKEYS_RESULT_INIT;
|
||||
numkeys = getKeysFromCommand(mcmd,margv,margc,&result);
|
||||
keyindex = result.keys;
|
||||
|
||||
for (j = 0; j < numkeys; j++) {
|
||||
robj *thiskey = margv[keyindex[j]];
|
||||
int thisslot = keyHashSlot((char*)thiskey->ptr,
|
||||
@ -5662,7 +5665,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
|
||||
* not trapped earlier in processCommand(). Report the same
|
||||
* error to the client. */
|
||||
if (n == NULL) {
|
||||
getKeysFreeResult(keyindex);
|
||||
getKeysFreeResult(&result);
|
||||
if (error_code)
|
||||
*error_code = CLUSTER_REDIR_DOWN_UNBOUND;
|
||||
return NULL;
|
||||
@ -5686,7 +5689,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
|
||||
if (!equalStringObjects(firstkey,thiskey)) {
|
||||
if (slot != thisslot) {
|
||||
/* Error: multiple keys from different slots. */
|
||||
getKeysFreeResult(keyindex);
|
||||
getKeysFreeResult(&result);
|
||||
if (error_code)
|
||||
*error_code = CLUSTER_REDIR_CROSS_SLOT;
|
||||
return NULL;
|
||||
@ -5705,7 +5708,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
|
||||
missing_keys++;
|
||||
}
|
||||
}
|
||||
getKeysFreeResult(keyindex);
|
||||
getKeysFreeResult(&result);
|
||||
}
|
||||
|
||||
/* No key at all in command? then we can serve the request
|
||||
|
169
src/db.c
169
src/db.c
@ -1314,27 +1314,54 @@ int expireIfNeeded(redisDb *db, robj *key) {
|
||||
/* -----------------------------------------------------------------------------
|
||||
* API to get key arguments from commands
|
||||
* ---------------------------------------------------------------------------*/
|
||||
#define MAX_KEYS_BUFFER 256
|
||||
static int getKeysTempBuffer[MAX_KEYS_BUFFER];
|
||||
|
||||
/* Prepare the getKeysResult struct to hold numkeys, either by using the
|
||||
* pre-allocated keysbuf or by allocating a new array on the heap.
|
||||
*
|
||||
* This function must be called at least once before starting to populate
|
||||
* the result, and can be called repeatedly to enlarge the result array.
|
||||
*/
|
||||
int *getKeysPrepareResult(getKeysResult *result, int numkeys) {
|
||||
/* GETKEYS_RESULT_INIT initializes keys to NULL, point it to the pre-allocated stack
|
||||
* buffer here. */
|
||||
if (!result->keys) {
|
||||
serverAssert(!result->numkeys);
|
||||
result->keys = result->keysbuf;
|
||||
}
|
||||
|
||||
/* Resize if necessary */
|
||||
if (numkeys > result->size) {
|
||||
if (result->keys != result->keysbuf) {
|
||||
/* We're not using a static buffer, just (re)alloc */
|
||||
result->keys = zrealloc(result->keys, numkeys * sizeof(int));
|
||||
} else {
|
||||
/* We are using a static buffer, copy its contents */
|
||||
result->keys = zmalloc(numkeys * sizeof(int));
|
||||
if (result->numkeys)
|
||||
memcpy(result->keys, result->keysbuf, result->numkeys * sizeof(int));
|
||||
}
|
||||
result->size = numkeys;
|
||||
}
|
||||
|
||||
return result->keys;
|
||||
}
|
||||
|
||||
/* The base case is to use the keys position as given in the command table
|
||||
* (firstkey, lastkey, step). */
|
||||
int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, int *numkeys) {
|
||||
int getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, getKeysResult *result) {
|
||||
int j, i = 0, last, *keys;
|
||||
UNUSED(argv);
|
||||
|
||||
if (cmd->firstkey == 0) {
|
||||
*numkeys = 0;
|
||||
return NULL;
|
||||
result->numkeys = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
last = cmd->lastkey;
|
||||
if (last < 0) last = argc+last;
|
||||
|
||||
int count = ((last - cmd->firstkey)+1);
|
||||
keys = getKeysTempBuffer;
|
||||
if (count > MAX_KEYS_BUFFER)
|
||||
keys = zmalloc(sizeof(int)*count);
|
||||
keys = getKeysPrepareResult(result, count);
|
||||
|
||||
for (j = cmd->firstkey; j <= last; j += cmd->keystep) {
|
||||
if (j >= argc) {
|
||||
@ -1345,17 +1372,17 @@ int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, in
|
||||
* return no keys and expect the command implementation to report
|
||||
* an arity or syntax error. */
|
||||
if (cmd->flags & CMD_MODULE || cmd->arity < 0) {
|
||||
getKeysFreeResult(keys);
|
||||
*numkeys = 0;
|
||||
return NULL;
|
||||
getKeysFreeResult(result);
|
||||
result->numkeys = 0;
|
||||
return 0;
|
||||
} else {
|
||||
serverPanic("Redis built-in command declared keys positions not matching the arity requirements.");
|
||||
}
|
||||
}
|
||||
keys[i++] = j;
|
||||
}
|
||||
*numkeys = i;
|
||||
return keys;
|
||||
result->numkeys = i;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Return all the arguments that are keys in the command passed via argc / argv.
|
||||
@ -1369,20 +1396,20 @@ int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, in
|
||||
*
|
||||
* This function uses the command table if a command-specific helper function
|
||||
* is not required, otherwise it calls the command-specific function. */
|
||||
int *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
if (cmd->flags & CMD_MODULE_GETKEYS) {
|
||||
return moduleGetCommandKeysViaAPI(cmd,argv,argc,numkeys);
|
||||
return moduleGetCommandKeysViaAPI(cmd,argv,argc,result);
|
||||
} else if (!(cmd->flags & CMD_MODULE) && cmd->getkeys_proc) {
|
||||
return cmd->getkeys_proc(cmd,argv,argc,numkeys);
|
||||
return cmd->getkeys_proc(cmd,argv,argc,result);
|
||||
} else {
|
||||
return getKeysUsingCommandTable(cmd,argv,argc,numkeys);
|
||||
return getKeysUsingCommandTable(cmd,argv,argc,result);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the result of getKeysFromCommand. */
|
||||
void getKeysFreeResult(int *result) {
|
||||
if (result != getKeysTempBuffer)
|
||||
zfree(result);
|
||||
void getKeysFreeResult(getKeysResult *result) {
|
||||
if (result && result->keys != result->keysbuf)
|
||||
zfree(result->keys);
|
||||
}
|
||||
|
||||
/* Helper function to extract keys from following commands:
|
||||
@ -1397,43 +1424,42 @@ void getKeysFreeResult(int *result) {
|
||||
* 'firstKeyOfs': firstkey index.
|
||||
* 'keyStep': the interval of each key, usually this value is 1.
|
||||
* */
|
||||
int *genericGetKeys(int storeKeyOfs, int keyCountOfs, int firstKeyOfs, int keyStep,
|
||||
robj **argv, int argc, int *numkeys) {
|
||||
int genericGetKeys(int storeKeyOfs, int keyCountOfs, int firstKeyOfs, int keyStep,
|
||||
robj **argv, int argc, getKeysResult *result) {
|
||||
int i, num, *keys;
|
||||
|
||||
num = atoi(argv[keyCountOfs]->ptr);
|
||||
/* Sanity check. Don't return any key if the command is going to
|
||||
* reply with syntax error. (no input keys). */
|
||||
if (num < 1 || num > (argc - firstKeyOfs)/keyStep) {
|
||||
*numkeys = 0;
|
||||
return NULL;
|
||||
result->numkeys = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
keys = getKeysTempBuffer;
|
||||
*numkeys = storeKeyOfs ? num + 1 : num;
|
||||
if (*numkeys > MAX_KEYS_BUFFER)
|
||||
keys = zmalloc(sizeof(int)*(*numkeys));
|
||||
int numkeys = storeKeyOfs ? num + 1 : num;
|
||||
keys = getKeysPrepareResult(result, numkeys);
|
||||
result->numkeys = numkeys;
|
||||
|
||||
/* Add all key positions for argv[firstKeyOfs...n] to keys[] */
|
||||
for (i = 0; i < num; i++) keys[i] = firstKeyOfs+(i*keyStep);
|
||||
|
||||
if (storeKeyOfs) keys[num] = storeKeyOfs;
|
||||
return keys;
|
||||
return result->numkeys;
|
||||
}
|
||||
|
||||
int *zunionInterStoreGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int zunionInterStoreGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
UNUSED(cmd);
|
||||
return genericGetKeys(1, 2, 3, 1, argv, argc, numkeys);
|
||||
return genericGetKeys(1, 2, 3, 1, argv, argc, result);
|
||||
}
|
||||
|
||||
int *zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
UNUSED(cmd);
|
||||
return genericGetKeys(0, 1, 2, 1, argv, argc, numkeys);
|
||||
return genericGetKeys(0, 1, 2, 1, argv, argc, result);
|
||||
}
|
||||
|
||||
int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
UNUSED(cmd);
|
||||
return genericGetKeys(0, 2, 3, 1, argv, argc, numkeys);
|
||||
return genericGetKeys(0, 2, 3, 1, argv, argc, result);
|
||||
}
|
||||
|
||||
/* Helper function to extract keys from the SORT command.
|
||||
@ -1443,13 +1469,12 @@ int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
|
||||
* The first argument of SORT is always a key, however a list of options
|
||||
* follow in SQL-alike style. Here we parse just the minimum in order to
|
||||
* correctly identify keys in the "STORE" option. */
|
||||
int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
int i, j, num, *keys, found_store = 0;
|
||||
UNUSED(cmd);
|
||||
|
||||
num = 0;
|
||||
keys = getKeysTempBuffer; /* Alloc 2 places for the worst case. */
|
||||
|
||||
keys = getKeysPrepareResult(result, 2); /* Alloc 2 places for the worst case. */
|
||||
keys[num++] = 1; /* <sort-key> is always present. */
|
||||
|
||||
/* Search for STORE option. By default we consider options to don't
|
||||
@ -1481,11 +1506,11 @@ int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
|
||||
}
|
||||
}
|
||||
}
|
||||
*numkeys = num + found_store;
|
||||
return keys;
|
||||
result->numkeys = num + found_store;
|
||||
return result->numkeys;
|
||||
}
|
||||
|
||||
int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
int i, num, first, *keys;
|
||||
UNUSED(cmd);
|
||||
|
||||
@ -1506,20 +1531,17 @@ int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkey
|
||||
}
|
||||
}
|
||||
|
||||
keys = getKeysTempBuffer;
|
||||
if (num>MAX_KEYS_BUFFER)
|
||||
keys = zmalloc(sizeof(int)*num);
|
||||
|
||||
keys = getKeysPrepareResult(result, num);
|
||||
for (i = 0; i < num; i++) keys[i] = first+i;
|
||||
*numkeys = num;
|
||||
return keys;
|
||||
result->numkeys = num;
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Helper function to extract keys from following commands:
|
||||
* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
|
||||
* [COUNT count] [STORE key] [STOREDIST key]
|
||||
* GEORADIUSBYMEMBER key member radius unit ... options ... */
|
||||
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
int i, num, *keys;
|
||||
UNUSED(cmd);
|
||||
|
||||
@ -1542,24 +1564,21 @@ int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numk
|
||||
* argv[1] = key,
|
||||
* argv[5...n] = stored key if present
|
||||
*/
|
||||
keys = getKeysTempBuffer;
|
||||
if (num>MAX_KEYS_BUFFER)
|
||||
keys = zmalloc(sizeof(int) * num);
|
||||
keys = getKeysPrepareResult(result, num);
|
||||
|
||||
/* Add all key positions to keys[] */
|
||||
keys[0] = 1;
|
||||
if(num > 1) {
|
||||
keys[1] = stored_key;
|
||||
}
|
||||
*numkeys = num;
|
||||
return keys;
|
||||
result->numkeys = num;
|
||||
return num;
|
||||
}
|
||||
|
||||
/* LCS ... [KEYS <key1> <key2>] ... */
|
||||
int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
|
||||
{
|
||||
int lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
int i;
|
||||
int *keys = getKeysTempBuffer;
|
||||
int *keys = getKeysPrepareResult(result, 2);
|
||||
UNUSED(cmd);
|
||||
|
||||
/* We need to parse the options of the command in order to check for the
|
||||
@ -1573,33 +1592,32 @@ int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
|
||||
} else if (!strcasecmp(arg, "keys") && moreargs >= 2) {
|
||||
keys[0] = i+1;
|
||||
keys[1] = i+2;
|
||||
*numkeys = 2;
|
||||
return keys;
|
||||
result->numkeys = 2;
|
||||
return result->numkeys;
|
||||
}
|
||||
}
|
||||
*numkeys = 0;
|
||||
return keys;
|
||||
result->numkeys = 0;
|
||||
return result->numkeys;
|
||||
}
|
||||
|
||||
/* Helper function to extract keys from memory command.
|
||||
* MEMORY USAGE <key> */
|
||||
int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int *keys;
|
||||
int memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
UNUSED(cmd);
|
||||
|
||||
getKeysPrepareResult(result, 1);
|
||||
if (argc >= 3 && !strcasecmp(argv[1]->ptr,"usage")) {
|
||||
keys = getKeysTempBuffer;
|
||||
keys[0] = 2;
|
||||
*numkeys = 1;
|
||||
return keys;
|
||||
result->keys[0] = 2;
|
||||
result->numkeys = 1;
|
||||
return result->numkeys;
|
||||
}
|
||||
*numkeys = 0;
|
||||
return NULL;
|
||||
result->numkeys = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XREAD [BLOCK <milliseconds>] [COUNT <count>] [GROUP <groupname> <ttl>]
|
||||
* STREAMS key_1 key_2 ... key_N ID_1 ID_2 ... ID_N */
|
||||
int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
int i, num = 0, *keys;
|
||||
UNUSED(cmd);
|
||||
|
||||
@ -1629,19 +1647,16 @@ int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
|
||||
|
||||
/* Syntax error. */
|
||||
if (streams_pos == -1 || num == 0 || num % 2 != 0) {
|
||||
*numkeys = 0;
|
||||
return NULL;
|
||||
result->numkeys = 0;
|
||||
return 0;
|
||||
}
|
||||
num /= 2; /* We have half the keys as there are arguments because
|
||||
there are also the IDs, one per key. */
|
||||
|
||||
keys = getKeysTempBuffer;
|
||||
if (num>MAX_KEYS_BUFFER)
|
||||
keys = zmalloc(sizeof(int) * num);
|
||||
|
||||
keys = getKeysPrepareResult(result, num);
|
||||
for (i = streams_pos+1; i < argc-num; i++) keys[i-streams_pos-1] = i;
|
||||
*numkeys = num;
|
||||
return keys;
|
||||
result->numkeys = num;
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Slot to Key API. This is used by Redis Cluster in order to obtain in
|
||||
|
33
src/module.c
33
src/module.c
@ -147,8 +147,7 @@ struct RedisModuleCtx {
|
||||
on keys. */
|
||||
|
||||
/* Used if there is the REDISMODULE_CTX_KEYS_POS_REQUEST flag set. */
|
||||
int *keys_pos;
|
||||
int keys_count;
|
||||
getKeysResult *keys_result;
|
||||
|
||||
struct RedisModulePoolAllocBlock *pa_head;
|
||||
redisOpArray saved_oparray; /* When propagating commands in a callback
|
||||
@ -158,7 +157,7 @@ struct RedisModuleCtx {
|
||||
};
|
||||
typedef struct RedisModuleCtx RedisModuleCtx;
|
||||
|
||||
#define REDISMODULE_CTX_INIT {(void*)(unsigned long)&RM_GetApi, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, 0, NULL, {0}}
|
||||
#define REDISMODULE_CTX_INIT {(void*)(unsigned long)&RM_GetApi, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL, {0}}
|
||||
#define REDISMODULE_CTX_MULTI_EMITTED (1<<0)
|
||||
#define REDISMODULE_CTX_AUTO_MEMORY (1<<1)
|
||||
#define REDISMODULE_CTX_KEYS_POS_REQUEST (1<<2)
|
||||
@ -665,18 +664,24 @@ void RedisModuleCommandDispatcher(client *c) {
|
||||
* 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) {
|
||||
int moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
|
||||
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;
|
||||
|
||||
/* Initialize getKeysResult */
|
||||
getKeysPrepareResult(result, MAX_KEYS_BUFFER);
|
||||
ctx.keys_result = result;
|
||||
|
||||
cp->func(&ctx,(void**)argv,argc);
|
||||
int *res = ctx.keys_pos;
|
||||
if (numkeys) *numkeys = ctx.keys_count;
|
||||
/* We currently always use the array allocated by RM_KeyAtPos() and don't try
|
||||
* to optimize for the pre-allocated buffer.
|
||||
*/
|
||||
moduleFreeContext(&ctx);
|
||||
return res;
|
||||
return result->numkeys;
|
||||
}
|
||||
|
||||
/* Return non-zero if a module command, that was declared with the
|
||||
@ -701,10 +706,18 @@ int RM_IsKeysPositionRequest(RedisModuleCtx *ctx) {
|
||||
* 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 (!(ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST) || !ctx->keys_result) 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;
|
||||
|
||||
getKeysResult *res = ctx->keys_result;
|
||||
|
||||
/* Check overflow */
|
||||
if (res->numkeys == res->size) {
|
||||
int newsize = res->size + (res->size > 8192 ? 8192 : res->size);
|
||||
getKeysPrepareResult(res, newsize);
|
||||
}
|
||||
|
||||
res->keys[res->numkeys++] = pos;
|
||||
}
|
||||
|
||||
/* Helper for RM_CreateCommand(). Turns a string representing command
|
||||
|
14
src/server.c
14
src/server.c
@ -3273,7 +3273,7 @@ struct redisCommand *lookupCommand(sds name) {
|
||||
return dictFetchValue(server.commands, name);
|
||||
}
|
||||
|
||||
struct redisCommand *lookupCommandByCString(char *s) {
|
||||
struct redisCommand *lookupCommandByCString(const char *s) {
|
||||
struct redisCommand *cmd;
|
||||
sds name = sdsnew(s);
|
||||
|
||||
@ -4142,7 +4142,8 @@ NULL
|
||||
addReplyLongLong(c, dictSize(server.commands));
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"getkeys") && c->argc >= 3) {
|
||||
struct redisCommand *cmd = lookupCommand(c->argv[2]->ptr);
|
||||
int *keys, numkeys, j;
|
||||
getKeysResult result = GETKEYS_RESULT_INIT;
|
||||
int j;
|
||||
|
||||
if (!cmd) {
|
||||
addReplyError(c,"Invalid command specified");
|
||||
@ -4157,14 +4158,13 @@ NULL
|
||||
return;
|
||||
}
|
||||
|
||||
keys = getKeysFromCommand(cmd,c->argv+2,c->argc-2,&numkeys);
|
||||
if (!keys) {
|
||||
if (!getKeysFromCommand(cmd,c->argv+2,c->argc-2,&result)) {
|
||||
addReplyError(c,"Invalid arguments specified for command");
|
||||
} else {
|
||||
addReplyArrayLen(c,numkeys);
|
||||
for (j = 0; j < numkeys; j++) addReplyBulk(c,c->argv[keys[j]+2]);
|
||||
getKeysFreeResult(keys);
|
||||
addReplyArrayLen(c,result.numkeys);
|
||||
for (j = 0; j < result.numkeys; j++) addReplyBulk(c,c->argv[result.keys[j]+2]);
|
||||
}
|
||||
getKeysFreeResult(&result);
|
||||
} else {
|
||||
addReplySubcommandSyntaxError(c);
|
||||
}
|
||||
|
42
src/server.h
42
src/server.h
@ -1497,8 +1497,21 @@ typedef struct pubsubPattern {
|
||||
robj *pattern;
|
||||
} pubsubPattern;
|
||||
|
||||
#define MAX_KEYS_BUFFER 256
|
||||
|
||||
/* A result structure for the various getkeys function calls. It lists the
|
||||
* keys as indices to the provided argv.
|
||||
*/
|
||||
typedef struct {
|
||||
int keysbuf[MAX_KEYS_BUFFER]; /* Pre-allocated buffer, to save heap allocations */
|
||||
int *keys; /* Key indices array, points to keysbuf or heap */
|
||||
int numkeys; /* Number of key indices return */
|
||||
int size; /* Available array size */
|
||||
} getKeysResult;
|
||||
#define GETKEYS_RESULT_INIT { {0}, NULL, 0, MAX_KEYS_BUFFER }
|
||||
|
||||
typedef void redisCommandProc(client *c);
|
||||
typedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
typedef int redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
struct redisCommand {
|
||||
char *name;
|
||||
redisCommandProc *proc;
|
||||
@ -1608,7 +1621,7 @@ void moduleInitModulesSystem(void);
|
||||
void moduleInitModulesSystemLast(void);
|
||||
int moduleLoad(const char *path, void **argv, int argc);
|
||||
void moduleLoadFromQueue(void);
|
||||
int *moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
moduleType *moduleTypeLookupModuleByID(uint64_t id);
|
||||
void moduleTypeNameByID(char *name, uint64_t moduleid);
|
||||
void moduleFreeContext(struct RedisModuleCtx *ctx);
|
||||
@ -2020,7 +2033,7 @@ int processCommand(client *c);
|
||||
void setupSignalHandlers(void);
|
||||
void removeSignalHandlers(void);
|
||||
struct redisCommand *lookupCommand(sds name);
|
||||
struct redisCommand *lookupCommandByCString(char *s);
|
||||
struct redisCommand *lookupCommandByCString(const char *s);
|
||||
struct redisCommand *lookupCommandOrOriginal(sds name);
|
||||
void call(client *c, int flags);
|
||||
void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int flags);
|
||||
@ -2181,17 +2194,18 @@ size_t lazyfreeGetPendingObjectsCount(void);
|
||||
void freeObjAsync(robj *o);
|
||||
|
||||
/* API to get key arguments from commands */
|
||||
int *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
void getKeysFreeResult(int *result);
|
||||
int *zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys);
|
||||
int *zunionInterStoreGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys);
|
||||
int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *getKeysPrepareResult(getKeysResult *result, int numkeys);
|
||||
int getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
void getKeysFreeResult(getKeysResult *result);
|
||||
int zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, getKeysResult *result);
|
||||
int zunionInterStoreGetKeys(struct redisCommand *cmd,robj **argv, int argc, getKeysResult *result);
|
||||
int evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
int sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
int migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
int georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
int xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
int memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
int lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
|
||||
|
||||
/* Cluster */
|
||||
void clusterInit(void);
|
||||
|
@ -171,9 +171,14 @@ void trackingRememberKeys(client *c) {
|
||||
uint64_t caching_given = c->flags & CLIENT_TRACKING_CACHING;
|
||||
if ((optin && !caching_given) || (optout && caching_given)) return;
|
||||
|
||||
int numkeys;
|
||||
int *keys = getKeysFromCommand(c->cmd,c->argv,c->argc,&numkeys);
|
||||
if (keys == NULL) return;
|
||||
getKeysResult result = GETKEYS_RESULT_INIT;
|
||||
int numkeys = getKeysFromCommand(c->cmd,c->argv,c->argc,&result);
|
||||
if (!numkeys) {
|
||||
getKeysFreeResult(&result);
|
||||
return;
|
||||
}
|
||||
|
||||
int *keys = result.keys;
|
||||
|
||||
for(int j = 0; j < numkeys; j++) {
|
||||
int idx = keys[j];
|
||||
@ -188,7 +193,7 @@ void trackingRememberKeys(client *c) {
|
||||
if (raxTryInsert(ids,(unsigned char*)&c->id,sizeof(c->id),NULL,NULL))
|
||||
TrackingTableTotalItems++;
|
||||
}
|
||||
getKeysFreeResult(keys);
|
||||
getKeysFreeResult(&result);
|
||||
}
|
||||
|
||||
/* Given a key name, this function sends an invalidation message in the
|
||||
|
Loading…
x
Reference in New Issue
Block a user