mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-23 00:28:26 -05:00
redis-cli: Add -X option and extend --cluster call take arg from stdin (#9980)
There are two changes in this commit: 1. Add -X option to redis-cli. Currently `-x` can only be used to provide the last argument, so you can do `redis-cli dump keyname > key.dump`, and then do `redis-cli -x restore keyname 0 < key.dump`. But what if you want to add the replace argument (which comes last?). oran suggested adding such usage: `redis-cli -X <tag> restore keyname <tag> replace < key.dump` i.e. you're able to provide a string in the arguments that's gonna be substituted with the content from stdin. Note that the tag name should not conflict with others non-replaced args. And the -x and -X options are conflicting. Some usages: ``` [root]# echo mypasswd | src/redis-cli -X passwd_tag mset username myname password passwd_tag OK [root]# echo username > username.txt [root]# head -c -1 username.txt | src/redis-cli -X name_tag mget name_tag password 1) "myname" 2) "mypasswd\n" ``` 2. Handle the combination of both `-x` and `--cluster` or `-X` and `--cluster` Extend the broadcast option to receive the last arg or <tag> arg from the stdin. Now we can use `redis-cli -x --cluster call <host>:<port> cmd`, or `redis-cli -X <tag> --cluster call <host>:<port> cmd <tag>`. (support part of #9899)
This commit is contained in:
parent
5006eab552
commit
4836ae32c7
@ -179,6 +179,7 @@ typedef struct clusterManagerCommand {
|
|||||||
char *name;
|
char *name;
|
||||||
int argc;
|
int argc;
|
||||||
char **argv;
|
char **argv;
|
||||||
|
sds stdin_arg; /* arg from stdin. (-X option) */
|
||||||
int flags;
|
int flags;
|
||||||
int replicas;
|
int replicas;
|
||||||
char *from;
|
char *from;
|
||||||
@ -196,7 +197,7 @@ typedef struct clusterManagerCommand {
|
|||||||
int from_askpass;
|
int from_askpass;
|
||||||
} clusterManagerCommand;
|
} clusterManagerCommand;
|
||||||
|
|
||||||
static void createClusterManagerCommand(char *cmdname, int argc, char **argv);
|
static int createClusterManagerCommand(char *cmdname, int argc, char **argv);
|
||||||
|
|
||||||
|
|
||||||
static redisContext *context;
|
static redisContext *context;
|
||||||
@ -235,7 +236,9 @@ static struct config {
|
|||||||
int memkeys;
|
int memkeys;
|
||||||
unsigned memkeys_samples;
|
unsigned memkeys_samples;
|
||||||
int hotkeys;
|
int hotkeys;
|
||||||
int stdinarg; /* get last arg from stdin. (-x option) */
|
int stdin_lastarg; /* get last arg from stdin. (-x option) */
|
||||||
|
int stdin_tag_arg; /* get <tag> arg from stdin. (-X option) */
|
||||||
|
char *stdin_tag_name; /* Placeholder(tag name) for user input. */
|
||||||
int askpass;
|
int askpass;
|
||||||
int quoted_input; /* Force input args to be treated as quoted strings */
|
int quoted_input; /* Force input args to be treated as quoted strings */
|
||||||
int output; /* output mode, see OUTPUT_* defines */
|
int output; /* output mode, see OUTPUT_* defines */
|
||||||
@ -1562,7 +1565,10 @@ static int parseOptions(int argc, char **argv) {
|
|||||||
} else if (!strcmp(argv[i],"--help")) {
|
} else if (!strcmp(argv[i],"--help")) {
|
||||||
usage(0);
|
usage(0);
|
||||||
} else if (!strcmp(argv[i],"-x")) {
|
} else if (!strcmp(argv[i],"-x")) {
|
||||||
config.stdinarg = 1;
|
config.stdin_lastarg = 1;
|
||||||
|
} else if (!strcmp(argv[i], "-X") && !lastarg) {
|
||||||
|
config.stdin_tag_arg = 1;
|
||||||
|
config.stdin_tag_name = argv[++i];
|
||||||
} else if (!strcmp(argv[i],"-p") && !lastarg) {
|
} else if (!strcmp(argv[i],"-p") && !lastarg) {
|
||||||
config.conn_info.hostport = atoi(argv[++i]);
|
config.conn_info.hostport = atoi(argv[++i]);
|
||||||
} else if (!strcmp(argv[i],"-s") && !lastarg) {
|
} else if (!strcmp(argv[i],"-s") && !lastarg) {
|
||||||
@ -1678,7 +1684,8 @@ static int parseOptions(int argc, char **argv) {
|
|||||||
int j = i;
|
int j = i;
|
||||||
while (j < argc && argv[j][0] != '-') j++;
|
while (j < argc && argv[j][0] != '-') j++;
|
||||||
if (j > i) j--;
|
if (j > i) j--;
|
||||||
createClusterManagerCommand(cmd, j - i, argv + i + 1);
|
int err = createClusterManagerCommand(cmd, j - i, argv + i + 1);
|
||||||
|
if (err) exit(err);
|
||||||
i = j;
|
i = j;
|
||||||
} else if (!strcmp(argv[i],"--cluster") && lastarg) {
|
} else if (!strcmp(argv[i],"--cluster") && lastarg) {
|
||||||
usage(1);
|
usage(1);
|
||||||
@ -1841,6 +1848,11 @@ static int parseOptions(int argc, char **argv) {
|
|||||||
" line interface may not be safe.\n", stderr);
|
" line interface may not be safe.\n", stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.stdin_lastarg && config.stdin_tag_arg) {
|
||||||
|
fprintf(stderr, "Options -x and -X are mutually exclusive.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1885,7 +1897,8 @@ static void usage(int err) {
|
|||||||
" -n <db> Database number.\n"
|
" -n <db> Database number.\n"
|
||||||
" -2 Start session in RESP2 protocol mode.\n"
|
" -2 Start session in RESP2 protocol mode.\n"
|
||||||
" -3 Start session in RESP3 protocol mode.\n"
|
" -3 Start session in RESP3 protocol mode.\n"
|
||||||
" -x Read last argument from STDIN.\n"
|
" -x Read last argument from STDIN (see example below).\n"
|
||||||
|
" -X Read <tag> argument from STDIN (see example below).\n"
|
||||||
" -d <delimiter> Delimiter between response bulks for raw formatting (default: \\n).\n"
|
" -d <delimiter> Delimiter between response bulks for raw formatting (default: \\n).\n"
|
||||||
" -D <delimiter> Delimiter between responses for raw formatting (default: \\n).\n"
|
" -D <delimiter> Delimiter between responses for raw formatting (default: \\n).\n"
|
||||||
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
|
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
|
||||||
@ -1975,7 +1988,7 @@ static void usage(int err) {
|
|||||||
"\n"
|
"\n"
|
||||||
"Examples:\n"
|
"Examples:\n"
|
||||||
" cat /etc/passwd | redis-cli -x set mypasswd\n"
|
" cat /etc/passwd | redis-cli -x set mypasswd\n"
|
||||||
" redis-cli get mypasswd\n"
|
" redis-cli -D \"\" --raw dump key > key.dump && redis-cli -X dump_tag restore key2 0 dump_tag replace < key.dump\n"
|
||||||
" redis-cli -r 100 lpush mylist x\n"
|
" redis-cli -r 100 lpush mylist x\n"
|
||||||
" redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
|
" redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
|
||||||
" redis-cli --quoted-input set '\"null-\\x00-separated\"' value\n"
|
" redis-cli --quoted-input set '\"null-\\x00-separated\"' value\n"
|
||||||
@ -2290,14 +2303,33 @@ static void repl(void) {
|
|||||||
static int noninteractive(int argc, char **argv) {
|
static int noninteractive(int argc, char **argv) {
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
sds *sds_args = getSdsArrayFromArgv(argc, argv, config.quoted_input);
|
sds *sds_args = getSdsArrayFromArgv(argc, argv, config.quoted_input);
|
||||||
|
|
||||||
if (!sds_args) {
|
if (!sds_args) {
|
||||||
printf("Invalid quoted string\n");
|
printf("Invalid quoted string\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (config.stdinarg) {
|
|
||||||
|
if (config.stdin_lastarg) {
|
||||||
sds_args = sds_realloc(sds_args, (argc + 1) * sizeof(sds));
|
sds_args = sds_realloc(sds_args, (argc + 1) * sizeof(sds));
|
||||||
sds_args[argc] = readArgFromStdin();
|
sds_args[argc] = readArgFromStdin();
|
||||||
argc++;
|
argc++;
|
||||||
|
} else if (config.stdin_tag_arg) {
|
||||||
|
int i = 0, tag_match = 0;
|
||||||
|
|
||||||
|
for (; i < argc; i++) {
|
||||||
|
if (strcmp(config.stdin_tag_name, sds_args[i]) != 0) continue;
|
||||||
|
|
||||||
|
tag_match = 1;
|
||||||
|
sdsfree(sds_args[i]);
|
||||||
|
sds_args[i] = readArgFromStdin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tag_match) {
|
||||||
|
sdsfreesplitres(sds_args, argc);
|
||||||
|
fprintf(stderr, "Using -X option but stdin tag not match.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = issueCommand(argc, sds_args);
|
retval = issueCommand(argc, sds_args);
|
||||||
@ -2577,14 +2609,41 @@ clusterManagerOptionDef clusterManagerOptions[] = {
|
|||||||
|
|
||||||
static void getRDB(clusterManagerNode *node);
|
static void getRDB(clusterManagerNode *node);
|
||||||
|
|
||||||
static void createClusterManagerCommand(char *cmdname, int argc, char **argv) {
|
static int createClusterManagerCommand(char *cmdname, int argc, char **argv) {
|
||||||
clusterManagerCommand *cmd = &config.cluster_manager_command;
|
clusterManagerCommand *cmd = &config.cluster_manager_command;
|
||||||
cmd->name = cmdname;
|
cmd->name = cmdname;
|
||||||
cmd->argc = argc;
|
cmd->argc = argc;
|
||||||
cmd->argv = argc ? argv : NULL;
|
cmd->argv = argc ? argv : NULL;
|
||||||
if (isColorTerm()) cmd->flags |= CLUSTER_MANAGER_CMD_FLAG_COLOR;
|
if (isColorTerm()) cmd->flags |= CLUSTER_MANAGER_CMD_FLAG_COLOR;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (config.stdin_lastarg) {
|
||||||
|
char **new_argv = zmalloc(sizeof(char*) * (cmd->argc+1));
|
||||||
|
memcpy(new_argv, cmd->argv, sizeof(char*) * cmd->argc);
|
||||||
|
|
||||||
|
cmd->stdin_arg = readArgFromStdin();
|
||||||
|
new_argv[cmd->argc++] = cmd->stdin_arg;
|
||||||
|
cmd->argv = new_argv;
|
||||||
|
} else if (config.stdin_tag_arg) {
|
||||||
|
int i = 0, tag_match = 0;
|
||||||
|
cmd->stdin_arg = readArgFromStdin();
|
||||||
|
|
||||||
|
for (; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], config.stdin_tag_name) != 0) continue;
|
||||||
|
|
||||||
|
tag_match = 1;
|
||||||
|
cmd->argv[i] = (char *)cmd->stdin_arg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tag_match) {
|
||||||
|
sdsfree(cmd->stdin_arg);
|
||||||
|
fprintf(stderr, "Using -X option but stdin tag not match.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static clusterManagerCommandProc *validateClusterManagerCommand(void) {
|
static clusterManagerCommandProc *validateClusterManagerCommand(void) {
|
||||||
int i, commands_count = sizeof(clusterManagerCommands) /
|
int i, commands_count = sizeof(clusterManagerCommands) /
|
||||||
@ -5591,12 +5650,18 @@ static void clusterManagerMode(clusterManagerCommandProc *proc) {
|
|||||||
int argc = config.cluster_manager_command.argc;
|
int argc = config.cluster_manager_command.argc;
|
||||||
char **argv = config.cluster_manager_command.argv;
|
char **argv = config.cluster_manager_command.argv;
|
||||||
cluster_manager.nodes = NULL;
|
cluster_manager.nodes = NULL;
|
||||||
if (!proc(argc, argv)) goto cluster_manager_err;
|
int success = proc(argc, argv);
|
||||||
|
|
||||||
|
/* Initialized in createClusterManagerCommand. */
|
||||||
|
if (config.stdin_lastarg) {
|
||||||
|
zfree(config.cluster_manager_command.argv);
|
||||||
|
sdsfree(config.cluster_manager_command.stdin_arg);
|
||||||
|
} else if (config.stdin_tag_arg) {
|
||||||
|
sdsfree(config.cluster_manager_command.stdin_arg);
|
||||||
|
}
|
||||||
freeClusterManager();
|
freeClusterManager();
|
||||||
exit(0);
|
|
||||||
cluster_manager_err:
|
exit(success ? 0 : 1);
|
||||||
freeClusterManager();
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cluster Manager Commands */
|
/* Cluster Manager Commands */
|
||||||
@ -8355,7 +8420,9 @@ int main(int argc, char **argv) {
|
|||||||
config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT;
|
config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT;
|
||||||
config.bigkeys = 0;
|
config.bigkeys = 0;
|
||||||
config.hotkeys = 0;
|
config.hotkeys = 0;
|
||||||
config.stdinarg = 0;
|
config.stdin_lastarg = 0;
|
||||||
|
config.stdin_tag_arg = 0;
|
||||||
|
config.stdin_tag_name = NULL;
|
||||||
config.conn_info.auth = NULL;
|
config.conn_info.auth = NULL;
|
||||||
config.askpass = 0;
|
config.askpass = 0;
|
||||||
config.conn_info.user = NULL;
|
config.conn_info.user = NULL;
|
||||||
@ -8372,6 +8439,7 @@ int main(int argc, char **argv) {
|
|||||||
config.cluster_manager_command.name = NULL;
|
config.cluster_manager_command.name = NULL;
|
||||||
config.cluster_manager_command.argc = 0;
|
config.cluster_manager_command.argc = 0;
|
||||||
config.cluster_manager_command.argv = NULL;
|
config.cluster_manager_command.argv = NULL;
|
||||||
|
config.cluster_manager_command.stdin_arg = NULL;
|
||||||
config.cluster_manager_command.flags = 0;
|
config.cluster_manager_command.flags = 0;
|
||||||
config.cluster_manager_command.replicas = 0;
|
config.cluster_manager_command.replicas = 0;
|
||||||
config.cluster_manager_command.from = NULL;
|
config.cluster_manager_command.from = NULL;
|
||||||
|
@ -108,12 +108,20 @@ start_server {tags {"cli"}} {
|
|||||||
_run_cli [srv host] [srv port] $::dbnum {} {*}$args
|
_run_cli [srv host] [srv port] $::dbnum {} {*}$args
|
||||||
}
|
}
|
||||||
|
|
||||||
proc run_cli_with_input_pipe {cmd args} {
|
proc run_cli_with_input_pipe {mode cmd args} {
|
||||||
|
if {$mode == "x" } {
|
||||||
_run_cli [srv host] [srv port] $::dbnum [list pipe $cmd] -x {*}$args
|
_run_cli [srv host] [srv port] $::dbnum [list pipe $cmd] -x {*}$args
|
||||||
|
} elseif {$mode == "X"} {
|
||||||
|
_run_cli [srv host] [srv port] $::dbnum [list pipe $cmd] -X tag {*}$args
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc run_cli_with_input_file {path args} {
|
proc run_cli_with_input_file {mode path args} {
|
||||||
|
if {$mode == "x" } {
|
||||||
_run_cli [srv host] [srv port] $::dbnum [list path $path] -x {*}$args
|
_run_cli [srv host] [srv port] $::dbnum [list path $path] -x {*}$args
|
||||||
|
} elseif {$mode == "X"} {
|
||||||
|
_run_cli [srv host] [srv port] $::dbnum [list path $path] -X tag {*}$args
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc run_cli_host_port_db {host port db args} {
|
proc run_cli_host_port_db {host port db args} {
|
||||||
@ -201,14 +209,22 @@ start_server {tags {"cli"}} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test_tty_cli "Read last argument from pipe" {
|
test_tty_cli "Read last argument from pipe" {
|
||||||
assert_equal "OK" [run_cli_with_input_pipe "echo foo" set key]
|
assert_equal "OK" [run_cli_with_input_pipe x "echo foo" set key]
|
||||||
assert_equal "foo\n" [r get key]
|
assert_equal "foo\n" [r get key]
|
||||||
|
|
||||||
|
assert_equal "OK" [run_cli_with_input_pipe X "echo foo" set key2 tag]
|
||||||
|
assert_equal "foo\n" [r get key2]
|
||||||
}
|
}
|
||||||
|
|
||||||
test_tty_cli "Read last argument from file" {
|
test_tty_cli "Read last argument from file" {
|
||||||
set tmpfile [write_tmpfile "from file"]
|
set tmpfile [write_tmpfile "from file"]
|
||||||
assert_equal "OK" [run_cli_with_input_file $tmpfile set key]
|
|
||||||
|
assert_equal "OK" [run_cli_with_input_file x $tmpfile set key]
|
||||||
assert_equal "from file" [r get key]
|
assert_equal "from file" [r get key]
|
||||||
|
|
||||||
|
assert_equal "OK" [run_cli_with_input_file X $tmpfile set key2 tag]
|
||||||
|
assert_equal "from file" [r get key2]
|
||||||
|
|
||||||
file delete $tmpfile
|
file delete $tmpfile
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,14 +296,22 @@ if {!$::tls} { ;# fake_redis_node doesn't support TLS
|
|||||||
}
|
}
|
||||||
|
|
||||||
test_nontty_cli "Read last argument from pipe" {
|
test_nontty_cli "Read last argument from pipe" {
|
||||||
assert_equal "OK" [run_cli_with_input_pipe "echo foo" set key]
|
assert_equal "OK" [run_cli_with_input_pipe x "echo foo" set key]
|
||||||
assert_equal "foo\n" [r get key]
|
assert_equal "foo\n" [r get key]
|
||||||
|
|
||||||
|
assert_equal "OK" [run_cli_with_input_pipe X "echo foo" set key2 tag]
|
||||||
|
assert_equal "foo\n" [r get key2]
|
||||||
}
|
}
|
||||||
|
|
||||||
test_nontty_cli "Read last argument from file" {
|
test_nontty_cli "Read last argument from file" {
|
||||||
set tmpfile [write_tmpfile "from file"]
|
set tmpfile [write_tmpfile "from file"]
|
||||||
assert_equal "OK" [run_cli_with_input_file $tmpfile set key]
|
|
||||||
|
assert_equal "OK" [run_cli_with_input_file x $tmpfile set key]
|
||||||
assert_equal "from file" [r get key]
|
assert_equal "from file" [r get key]
|
||||||
|
|
||||||
|
assert_equal "OK" [run_cli_with_input_file X $tmpfile set key2 tag]
|
||||||
|
assert_equal "from file" [r get key2]
|
||||||
|
|
||||||
file delete $tmpfile
|
file delete $tmpfile
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,4 +423,12 @@ if {!$::tls} { ;# fake_redis_node doesn't support TLS
|
|||||||
|
|
||||||
file delete $cmds
|
file delete $cmds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Options -X with illegal argument" {
|
||||||
|
assert_error "*-x and -X are mutually exclusive*" {run_cli -x -X tag}
|
||||||
|
|
||||||
|
assert_error "*Unrecognized option or bad number*" {run_cli -X}
|
||||||
|
|
||||||
|
assert_error "*tag not match*" {run_cli_with_input_pipe X "echo foo" set key wrong_tag}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user