summaryrefslogtreecommitdiff
path: root/src/server.c
diff options
context:
space:
mode:
authorJason Elbaum <Jason.elbaum@redis.com>2023-03-30 19:03:56 +0300
committerGitHub <noreply@github.com>2023-03-30 19:03:56 +0300
commit1f76bb17ddcb2adc484bf82f1b839c45e264524f (patch)
treeec1f4d09166d1c2bd783c3668f2a4913aa8bcf5e /src/server.c
parent971b177fa338fe06cb67a930c6e54467d29ec44f (diff)
downloadredis-1f76bb17ddcb2adc484bf82f1b839c45e264524f.tar.gz
Reimplement cli hints based on command arg docs (#10515)
Now that the command argument specs are available at runtime (#9656), this PR addresses #8084 by implementing a complete solution for command-line hinting in `redis-cli`. It correctly handles nearly every case in Redis's complex command argument definitions, including `BLOCK` and `ONEOF` arguments, reordering of optional arguments, and repeated arguments (even when followed by mandatory arguments). It also validates numerically-typed arguments. It may not correctly handle all possible combinations of those, but overall it is quite robust. Arguments are only matched after the space bar is typed, so partial word matching is not supported - that proved to be more confusing than helpful. When the user's current input cannot be matched against the argument specs, hinting is disabled. Partial support has been implemented for legacy (pre-7.0) servers that do not support `COMMAND DOCS`, by falling back to a statically-compiled command argument table. On startup, if the server does not support `COMMAND DOCS`, `redis-cli` will now issue an `INFO SERVER` command to retrieve the server version (unless `HELLO` has already been sent, in which case the server version will be extracted from the reply to `HELLO`). The server version will be used to filter the commands and arguments in the command table, removing those not supported by that version of the server. However, the static table only includes core Redis commands, so with a legacy server hinting will not be supported for module commands. The auto generated help.h and the scripts that generates it are gone. Command and argument tables for the server and CLI use different structs, due primarily to the need to support different runtime data. In order to generate code for both, macros have been added to `commands.def` (previously `commands.c`) to make it possible to configure the code generation differently for different use cases (one linked with redis-server, and one with redis-cli). Also adding a basic testing framework for the command hints based on new (undocumented) command line options to `redis-cli`: `--test_hint 'INPUT'` prints out the command-line hint for a given input string, and `--test_hint_file <filename>` runs a suite of test cases for the hinting mechanism. The test suite is in `tests/assets/test_cli_hint_suite.txt`, and it is run from `tests/integration/redis-cli.tcl`. Co-authored-by: Oran Agra <oran@redislabs.com> Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
Diffstat (limited to 'src/server.c')
-rw-r--r--src/server.c57
1 files changed, 1 insertions, 56 deletions
diff --git a/src/server.c b/src/server.c
index 00dfcb776..951dd387c 100644
--- a/src/server.c
+++ b/src/server.c
@@ -2943,21 +2943,6 @@ void setImplicitACLCategories(struct redisCommand *c) {
c->acl_categories |= ACL_CATEGORY_SLOW;
}
-/* Recursively populate the args structure (setting num_args to the number of
- * subargs) and return the number of args. */
-int populateArgsStructure(struct redisCommandArg *args) {
- if (!args)
- return 0;
- int count = 0;
- while (args->name) {
- serverAssert(count < INT_MAX);
- args->num_args = populateArgsStructure(args->subargs);
- count++;
- args++;
- }
- return count;
-}
-
/* Recursively populate the command structure.
*
* On success, the function return C_OK. Otherwise C_ERR is returned and we won't
@@ -2975,28 +2960,10 @@ int populateCommandStructure(struct redisCommand *c) {
* set of flags. */
setImplicitACLCategories(c);
- /* Redis commands don't need more args than STATIC_KEY_SPECS_NUM (Number of keys
- * specs can be greater than STATIC_KEY_SPECS_NUM only for module commands) */
- c->key_specs = c->key_specs_static;
- c->key_specs_max = STATIC_KEY_SPECS_NUM;
-
/* We start with an unallocated histogram and only allocate memory when a command
* has been issued for the first time */
c->latency_histogram = NULL;
- for (int i = 0; i < STATIC_KEY_SPECS_NUM; i++) {
- if (c->key_specs[i].begin_search_type == KSPEC_BS_INVALID)
- break;
- c->key_specs_num++;
- }
-
- /* Count things so we don't have to use deferred reply in COMMAND reply. */
- while (c->history && c->history[c->num_history].since)
- c->num_history++;
- while (c->tips && c->tips[c->num_tips])
- c->num_tips++;
- c->num_args = populateArgsStructure(c->args);
-
/* Handle the legacy range spec and the "movablekeys" flag (must be done after populating all key specs). */
populateCommandLegacyRangeSpec(c);
@@ -4860,28 +4827,6 @@ void addReplyCommandSubCommands(client *c, struct redisCommand *cmd, void (*repl
dictReleaseIterator(di);
}
-/* Must match redisCommandGroup */
-const char *COMMAND_GROUP_STR[] = {
- "generic",
- "string",
- "list",
- "set",
- "sorted-set",
- "hash",
- "pubsub",
- "transactions",
- "connection",
- "server",
- "scripting",
- "hyperloglog",
- "cluster",
- "sentinel",
- "geo",
- "stream",
- "bitmap",
- "module"
-};
-
/* Output the representation of a Redis command. Used by the COMMAND command and COMMAND INFO. */
void addReplyCommandInfo(client *c, struct redisCommand *cmd) {
if (!cmd) {
@@ -4940,7 +4885,7 @@ void addReplyCommandDocs(client *c, struct redisCommand *cmd) {
/* Always have the group, for module commands the group is always "module". */
addReplyBulkCString(c, "group");
- addReplyBulkCString(c, COMMAND_GROUP_STR[cmd->group]);
+ addReplyBulkCString(c, commandGroupStr(cmd->group));
if (cmd->complexity) {
addReplyBulkCString(c, "complexity");