Initialize help when using redis-cli help or redis-cli ? (#10382)

The following usage will output an empty newline:
```
> redis-cli help set
empty line
```

The reason is that in interactive mode, we have called
`cliInitHelp`, which initializes help.

When using `redis-cli help xxx` or `redis-cli help ? xxx`,
we can't match the command due to empty `helpEntries`,
so we output an empty newline.

In this commit, we will call `cliInitHelp` to init the help.
Note that in this case, we need to call `cliInitHelp` (COMMAND DOCS)
every time, which i think is acceptable.

So now the output will look like:
```
[redis]# src/redis-cli help get

  GET key
  summary: Get the value of a key
  since: 1.0.0
  group: string

[redis]#
```

Fixes #10378

This PR also fix a redis-cli crash when using `--ldb --eval`:
```
[root]# src/redis-cli --ldb --eval test.lua test 1
Lua debugging session started, please use:
quit    -- End the session.
restart -- Restart the script in debug mode again.
help    -- Show Lua script debugging commands.

* Stopped at 1, stop reason = step over
-> 1   local num = redis.call('GET', KEYS[1]);
redis-cli: redis-cli.c:718: cliCountCommands: Assertion
`commandTable->element[i]->type == 1' failed.
Aborted
```
Because in ldb mode, `COMMAND DOCS` or `COMMAND` will
return an array, only with one element, and the type
is `REDIS_REPLY_STATUS`, the result is `<error> Unknown
Redis Lua debugger command or wrong number of arguments`.

So if we are in the ldb mode, and init the Redis HELP, we
will get the wrong response and crash the redis-cli.
In ldb mode we don't initialize HELP, help is only initialized
after the lua debugging session ends.

It was broken in #10043
This commit is contained in:
Binbin 2022-03-11 00:20:01 +08:00 committed by GitHub
parent 11b071a22b
commit 1797330e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -282,7 +282,7 @@ static void usage(int err);
static void slaveMode(void);
char *redisGitSHA1(void);
char *redisGitDirty(void);
static int cliConnect(int force);
static int cliConnect(int flags);
static char *getInfoField(char *info, char *field);
static long getLongInfoField(char *info, char *field);
@ -800,7 +800,12 @@ static void cliInitHelp(void) {
redisReply *commandTable;
dict *groups;
if (cliConnect(CC_QUIET) == REDIS_ERR) return;
if (cliConnect(CC_QUIET) == REDIS_ERR) {
/* Can not connect to the server, but we still want to provide
* help, generate it only from the old help.h data instead. */
cliOldInitHelp();
return;
}
commandTable = redisCommand(context, "COMMAND DOCS");
if (commandTable == NULL || commandTable->type == REDIS_REPLY_ERROR) {
/* New COMMAND DOCS subcommand not supported - generate help from old help.h data instead. */
@ -810,7 +815,7 @@ static void cliInitHelp(void) {
return;
};
if (commandTable->type != REDIS_REPLY_MAP && commandTable->type != REDIS_REPLY_ARRAY) return;
/* Scan the array reported by COMMAND DOCS and fill in the entries */
helpEntriesLen = cliCountCommands(commandTable);
helpEntries = zmalloc(sizeof(helpEntry)*helpEntriesLen);
@ -870,6 +875,12 @@ static void cliOutputHelp(int argc, char **argv) {
group = argv[0]+1;
}
if (helpEntries == NULL) {
/* Initialize the help using the results of the COMMAND command.
* In case we are using redis-cli help XXX, we need to init it. */
cliInitHelp();
}
assert(argc > 0);
for (i = 0; i < helpEntriesLen; i++) {
entry = &helpEntries[i];
@ -1713,12 +1724,6 @@ static int cliSendCommand(int argc, char **argv, long repeat) {
size_t *argvlen;
int j, output_raw;
if (!config.eval_ldb && /* In debugging mode, let's pass "help" to Redis. */
(!strcasecmp(command,"help") || !strcasecmp(command,"?"))) {
cliOutputHelp(--argc, ++argv);
return REDIS_OK;
}
if (context == NULL) return REDIS_ERR;
output_raw = 0;
@ -2420,6 +2425,17 @@ static int confirmWithYes(char *msg, int ignore_force) {
}
static int issueCommandRepeat(int argc, char **argv, long repeat) {
/* In Lua debugging mode, we want to pass the "help" to Redis to get
* it's own HELP message, rather than handle it by the CLI, see ldbRepl.
*
* For the normal Redis HELP, we can process it without a connection. */
if (!config.eval_ldb &&
(!strcasecmp(argv[0],"help") || !strcasecmp(argv[0],"?")))
{
cliOutputHelp(--argc, ++argv);
return REDIS_OK;
}
while (1) {
if (config.cluster_reissue_command || context == NULL ||
context->err == REDIS_ERR_IO || context->err == REDIS_ERR_EOF)
@ -2439,6 +2455,8 @@ static int issueCommandRepeat(int argc, char **argv, long repeat) {
}
if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {
cliPrintContextError();
redisFree(context);
context = NULL;
return REDIS_ERR;
}
@ -2576,8 +2594,13 @@ static void repl(void) {
int argc;
sds *argv;
/* Initialize the help using the results of the COMMAND command. */
cliInitHelp();
/* There is no need to initialize redis HELP when we are in lua debugger mode.
* It has its own HELP and commands (COMMAND or COMMAND DOCS will fail and got nothing).
* We will initialize the redis HELP after the Lua debugging session ended.*/
if (!config.eval_ldb) {
/* Initialize the help using the results of the COMMAND command. */
cliInitHelp();
}
config.interactive = 1;
linenoiseSetMultiLine(1);
@ -2679,6 +2702,7 @@ static void repl(void) {
printf("\n(Lua debugging session ended%s)\n\n",
config.eval_ldb_sync ? "" :
" -- dataset changes rolled back");
cliInitHelp();
}
elapsed = mstime()-start_time;
@ -9005,10 +9029,11 @@ int main(int argc, char **argv) {
}
/* Otherwise, we have some arguments to execute */
if (cliConnect(0) != REDIS_OK) exit(1);
if (config.eval) {
if (cliConnect(0) != REDIS_OK) exit(1);
return evalMode(argc,argv);
} else {
cliConnect(CC_QUIET);
return noninteractive(argc,argv);
}
}