Merge pull request #54249 from rschu1ze/multiple_query_arguments

Allow multiple `--query` parameters in clickhouse-client/local.
This commit is contained in:
Robert Schulze 2023-09-07 21:20:07 +02:00 committed by GitHub
commit bff06d18aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 93 additions and 51 deletions

View File

@ -30,7 +30,7 @@ It may lack support for new features.
## Usage {#cli_usage} ## Usage {#cli_usage}
The client can be used in interactive and non-interactive (batch) mode. The client can be used in interactive and non-interactive (batch) mode.
### Gather your connection details ### Gather your connection details
<ConnectionDetails /> <ConnectionDetails />
@ -177,8 +177,8 @@ You can pass parameters to `clickhouse-client` (all parameters have a default va
- `--user, -u` The username. Default value: default. - `--user, -u` The username. Default value: default.
- `--password` The password. Default value: empty string. - `--password` The password. Default value: empty string.
- `--ask-password` - Prompt the user to enter a password. - `--ask-password` - Prompt the user to enter a password.
- `--query, -q` The query to process when using non-interactive mode. Cannot be used simultaneously with `--queries-file`. - `--query, -q` The query to process when using non-interactive mode. `--query` can be specified multiple times, e.g. `--query "SELECT 1" --query "SELECT 2"`. Cannot be used simultaneously with `--queries-file`.
- `--queries-file` file path with queries to execute. Cannot be used simultaneously with `--query`. - `--queries-file` file path with queries to execute. `--queries-file` can be specified multiple times, e.g. `--query queries1.sql --query queries2.sql`. Cannot be used simultaneously with `--query`.
- `--multiquery, -n` If specified, multiple queries separated by semicolons can be listed after the `--query` option. For convenience, it is also possible to omit `--query` and pass the queries directly after `--multiquery`. - `--multiquery, -n` If specified, multiple queries separated by semicolons can be listed after the `--query` option. For convenience, it is also possible to omit `--query` and pass the queries directly after `--multiquery`.
- `--multiline, -m` If specified, allow multiline queries (do not send the query on Enter). - `--multiline, -m` If specified, allow multiline queries (do not send the query on Enter).
- `--database, -d` Select the current default database. Default value: the current database from the server settings (default by default). - `--database, -d` Select the current default database. Default value: the current database from the server settings (default by default).

View File

@ -202,8 +202,8 @@ Arguments:
- `-S`, `--structure` — table structure for input data. - `-S`, `--structure` — table structure for input data.
- `--input-format` — input format, `TSV` by default. - `--input-format` — input format, `TSV` by default.
- `-f`, `--file` — path to data, `stdin` by default. - `-f`, `--file` — path to data, `stdin` by default.
- `-q`, `--query` — queries to execute with `;` as delimiter. Cannot be used simultaneously with `--queries-file`. - `-q`, `--query` — queries to execute with `;` as delimiter. `--query` can be specified multiple times, e.g. `--query "SELECT 1" --query "SELECT 2"`. Cannot be used simultaneously with `--queries-file`.
- `--queries-file` - file path with queries to execute. Cannot be used simultaneously with `--query`. - `--queries-file` - file path with queries to execute. `--queries-file` can be specified multiple times, e.g. `--query queries1.sql --query queries2.sql`. Cannot be used simultaneously with `--query`.
- `--multiquery, -n` If specified, multiple queries separated by semicolons can be listed after the `--query` option. For convenience, it is also possible to omit `--query` and pass the queries directly after `--multiquery`. - `--multiquery, -n` If specified, multiple queries separated by semicolons can be listed after the `--query` option. For convenience, it is also possible to omit `--query` and pass the queries directly after `--multiquery`.
- `-N`, `--table` — table name where to put output data, `table` by default. - `-N`, `--table` — table name where to put output data, `table` by default.
- `--format`, `--output-format` — output format, `TSV` by default. - `--format`, `--output-format` — output format, `TSV` by default.

View File

@ -128,7 +128,7 @@ $ clickhouse-client --param_tbl="numbers" --param_db="system" --param_col="numbe
- `--port` — порт для подключения, по умолчанию — 9000. Обратите внимание: для HTTP-интерфейса и нативного интерфейса используются разные порты. - `--port` — порт для подключения, по умолчанию — 9000. Обратите внимание: для HTTP-интерфейса и нативного интерфейса используются разные порты.
- `--user, -u` — имя пользователя, по умолчанию — default. - `--user, -u` — имя пользователя, по умолчанию — default.
- `--password` — пароль, по умолчанию — пустая строка. - `--password` — пароль, по умолчанию — пустая строка.
- `--query, -q` — запрос для выполнения, при использовании в неинтерактивном режиме. - `--query, -q` — запрос для выполнения, при использовании в неинтерактивном режиме. Допускается указание `--query` несколько раз (`--query "SELECT 1;" --query "SELECT 2;"...`).
- `--queries-file` - путь к файлу с запросами для выполнения. Необходимо указать только одну из опций: `query` или `queries-file`. - `--queries-file` - путь к файлу с запросами для выполнения. Необходимо указать только одну из опций: `query` или `queries-file`.
- `--database, -d` — выбрать текущую БД. Без указания значение берется из настроек сервера (по умолчанию — БД default). - `--database, -d` — выбрать текущую БД. Без указания значение берется из настроек сервера (по умолчанию — БД default).
- `--multiline, -m` — если указано — разрешить многострочные запросы, не отправлять запрос по нажатию Enter. - `--multiline, -m` — если указано — разрешить многострочные запросы, не отправлять запрос по нажатию Enter.

View File

@ -116,7 +116,7 @@ $ clickhouse-client --param_tuple_in_tuple="(10, ('dt', 10))" -q "SELECT * FROM
- `--port` 连接的端口默认值9000。注意HTTP接口以及TCP原生接口使用的是不同端口。 - `--port` 连接的端口默认值9000。注意HTTP接口以及TCP原生接口使用的是不同端口。
- `--user, -u` 用户名。 默认值:`default`。 - `--user, -u` 用户名。 默认值:`default`。
- `--password` 密码。 默认值:空字符串。 - `--password` 密码。 默认值:空字符串。
- `--query, -q` 使用非交互模式查询。 - `--query, -q` 使用非交互模式查询。 允许多次指定 `--query``--query "SELECT 1;" --query "SELECT 2;"...`)。
- `--database, -d` 默认当前操作的数据库. 默认值:服务端默认的配置(默认是`default`)。 - `--database, -d` 默认当前操作的数据库. 默认值:服务端默认的配置(默认是`default`)。
- `--multiline, -m` 如果指定允许多行语句查询Enter仅代表换行不代表查询语句完结 - `--multiline, -m` 如果指定允许多行语句查询Enter仅代表换行不代表查询语句完结
- `--multiquery, -n` 如果指定, 允许处理用`;`号分隔的多个查询,只在非交互模式下生效。 - `--multiquery, -n` 如果指定, 允许处理用`;`号分隔的多个查询,只在非交互模式下生效。

View File

@ -1189,7 +1189,7 @@ void Client::processOptions(const OptionsDescription & options_description,
void Client::processConfig() void Client::processConfig()
{ {
if (config().has("query") && config().has("queries-file")) if (!queries.empty() && config().has("queries-file"))
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Options '--query' and '--queries-file' cannot be specified at the same time"); throw Exception(ErrorCodes::BAD_ARGUMENTS, "Options '--query' and '--queries-file' cannot be specified at the same time");
/// Batch mode is enabled if one of the following is true: /// Batch mode is enabled if one of the following is true:
@ -1200,9 +1200,9 @@ void Client::processConfig()
/// - --queries-file command line option is present. /// - --queries-file command line option is present.
/// The value of the option is used as file with query (or of multiple queries) to execute. /// The value of the option is used as file with query (or of multiple queries) to execute.
delayed_interactive = config().has("interactive") && (config().has("query") || config().has("queries-file")); delayed_interactive = config().has("interactive") && (!queries.empty() || config().has("queries-file"));
if (stdin_is_a_tty if (stdin_is_a_tty
&& (delayed_interactive || (!config().has("query") && queries_files.empty()))) && (delayed_interactive || (queries.empty() && queries_files.empty())))
{ {
is_interactive = true; is_interactive = true;
} }

View File

@ -319,7 +319,7 @@ static bool checkIfStdinIsRegularFile()
std::string LocalServer::getInitialCreateTableQuery() std::string LocalServer::getInitialCreateTableQuery()
{ {
if (!config().has("table-structure") && !config().has("table-file") && !config().has("table-data-format") && (!checkIfStdinIsRegularFile() || !config().has("query"))) if (!config().has("table-structure") && !config().has("table-file") && !config().has("table-data-format") && (!checkIfStdinIsRegularFile() || queries.empty()))
return {}; return {};
auto table_name = backQuoteIfNeed(config().getString("table-name", "table")); auto table_name = backQuoteIfNeed(config().getString("table-name", "table"));
@ -461,7 +461,7 @@ try
if (first_time) if (first_time)
{ {
if (queries_files.empty() && !config().has("query")) if (queries_files.empty() && queries.empty())
{ {
std::cerr << "\033[31m" << "ClickHouse compiled in fuzzing mode." << "\033[0m" << std::endl; std::cerr << "\033[31m" << "ClickHouse compiled in fuzzing mode." << "\033[0m" << std::endl;
std::cerr << "\033[31m" << "You have to provide a query with --query or --queries-file option." << "\033[0m" << std::endl; std::cerr << "\033[31m" << "You have to provide a query with --query or --queries-file option." << "\033[0m" << std::endl;
@ -473,7 +473,7 @@ try
#else #else
is_interactive = stdin_is_a_tty is_interactive = stdin_is_a_tty
&& (config().hasOption("interactive") && (config().hasOption("interactive")
|| (!config().has("query") && !config().has("table-structure") && queries_files.empty() && !config().has("table-file"))); || (queries.empty() && !config().has("table-structure") && queries_files.empty() && !config().has("table-file")));
#endif #endif
if (!is_interactive) if (!is_interactive)
{ {
@ -569,10 +569,10 @@ void LocalServer::updateLoggerLevel(const String & logs_level)
void LocalServer::processConfig() void LocalServer::processConfig()
{ {
if (config().has("query") && config().has("queries-file")) if (!queries.empty() && config().has("queries-file"))
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Options '--query' and '--queries-file' cannot be specified at the same time"); throw Exception(ErrorCodes::BAD_ARGUMENTS, "Options '--query' and '--queries-file' cannot be specified at the same time");
delayed_interactive = config().has("interactive") && (config().has("query") || config().has("queries-file")); delayed_interactive = config().has("interactive") && (!queries.empty() || config().has("queries-file"));
if (is_interactive && !delayed_interactive) if (is_interactive && !delayed_interactive)
{ {
if (config().has("multiquery")) if (config().has("multiquery"))

View File

@ -2495,23 +2495,34 @@ void ClientBase::runNonInteractive()
return; return;
} }
String text; if (!queries.empty())
if (config().has("query"))
{ {
text += config().getRawString("query"); /// Poco configuration should not process substitutions in form of ${...} inside query. for (const auto & query : queries)
{
if (query_fuzzer_runs)
{
if (!processWithFuzzing(query))
return;
}
else
{
if (!processQueryText(query))
return;
}
}
} }
else else
{ {
/// If 'query' parameter is not set, read a query from stdin. /// If 'query' parameter is not set, read a query from stdin.
/// The query is read entirely into memory (streaming is disabled). /// The query is read entirely into memory (streaming is disabled).
ReadBufferFromFileDescriptor in(STDIN_FILENO); ReadBufferFromFileDescriptor in(STDIN_FILENO);
String text;
readStringUntilEOF(text, in); readStringUntilEOF(text, in);
if (query_fuzzer_runs)
processWithFuzzing(text);
else
processQueryText(text);
} }
if (query_fuzzer_runs)
processWithFuzzing(text);
else
processQueryText(text);
} }
@ -2680,8 +2691,8 @@ void ClientBase::init(int argc, char ** argv)
stderr_is_a_tty = isatty(STDERR_FILENO); stderr_is_a_tty = isatty(STDERR_FILENO);
terminal_width = getTerminalWidth(); terminal_width = getTerminalWidth();
Arguments common_arguments{""}; /// 0th argument is ignored.
std::vector<Arguments> external_tables_arguments; std::vector<Arguments> external_tables_arguments;
Arguments common_arguments = {""}; /// 0th argument is ignored.
std::vector<Arguments> hosts_and_ports_arguments; std::vector<Arguments> hosts_and_ports_arguments;
readArguments(argc, argv, common_arguments, external_tables_arguments, hosts_and_ports_arguments); readArguments(argc, argv, common_arguments, external_tables_arguments, hosts_and_ports_arguments);
@ -2699,7 +2710,6 @@ void ClientBase::init(int argc, char ** argv)
} }
po::variables_map options;
OptionsDescription options_description; OptionsDescription options_description;
options_description.main_description.emplace(createOptionsDescription("Main options", terminal_width)); options_description.main_description.emplace(createOptionsDescription("Main options", terminal_width));
@ -2711,9 +2721,8 @@ void ClientBase::init(int argc, char ** argv)
("config-file,C", po::value<std::string>(), "config-file path") ("config-file,C", po::value<std::string>(), "config-file path")
("query,q", po::value<std::string>(), "query") ("query,q", po::value<std::vector<std::string>>()->multitoken(), R"(query; can be specified multiple times (--query "SELECT 1" --query "SELECT 2"...))")
("queries-file", po::value<std::vector<std::string>>()->multitoken(), ("queries-file", po::value<std::vector<std::string>>()->multitoken(), "file path with queries to execute; multiple files can be specified (--queries-file file1 file2...)")
"file path with queries to execute; multiple files can be specified (--queries-file file1 file2...)")
("multiquery,n", "If specified, multiple queries separated by semicolons can be listed after --query. For convenience, it is also possible to omit --query and pass the queries directly after --multiquery.") ("multiquery,n", "If specified, multiple queries separated by semicolons can be listed after --query. For convenience, it is also possible to omit --query and pass the queries directly after --multiquery.")
("multiline,m", "If specified, allow multiline queries (do not send the query on Enter)") ("multiline,m", "If specified, allow multiline queries (do not send the query on Enter)")
("database,d", po::value<std::string>(), "database") ("database,d", po::value<std::string>(), "database")
@ -2734,8 +2743,7 @@ void ClientBase::init(int argc, char ** argv)
("log-level", po::value<std::string>(), "log level") ("log-level", po::value<std::string>(), "log level")
("server_logs_file", po::value<std::string>(), "put server logs into specified file") ("server_logs_file", po::value<std::string>(), "put server logs into specified file")
("suggestion_limit", po::value<int>()->default_value(10000), ("suggestion_limit", po::value<int>()->default_value(10000), "Suggestion limit for how many databases, tables and columns to fetch.")
"Suggestion limit for how many databases, tables and columns to fetch.")
("format,f", po::value<std::string>(), "default output format") ("format,f", po::value<std::string>(), "default output format")
("vertical,E", "vertical output format, same as --format=Vertical or FORMAT Vertical or \\G at end of command") ("vertical,E", "vertical output format, same as --format=Vertical or FORMAT Vertical or \\G at end of command")
@ -2773,6 +2781,7 @@ void ClientBase::init(int argc, char ** argv)
std::transform(external_options.begin(), external_options.end(), std::back_inserter(cmd_options), getter); std::transform(external_options.begin(), external_options.end(), std::back_inserter(cmd_options), getter);
} }
po::variables_map options;
parseAndCheckOptions(options_description, options, common_arguments); parseAndCheckOptions(options_description, options, common_arguments);
po::notify(options); po::notify(options);
@ -2800,7 +2809,7 @@ void ClientBase::init(int argc, char ** argv)
if (options.count("time")) if (options.count("time"))
print_time_to_stderr = true; print_time_to_stderr = true;
if (options.count("query")) if (options.count("query"))
config().setString("query", options["query"].as<std::string>()); queries = options["query"].as<std::vector<std::string>>();
if (options.count("query_id")) if (options.count("query_id"))
config().setString("query_id", options["query_id"].as<std::string>()); config().setString("query_id", options["query_id"].as<std::string>());
if (options.count("database")) if (options.count("database"))

View File

@ -202,6 +202,7 @@ protected:
std::optional<Suggest> suggest; std::optional<Suggest> suggest;
bool load_suggestions = false; bool load_suggestions = false;
std::vector<String> queries; /// Queries passed via '--query'
std::vector<String> queries_files; /// If not empty, queries will be read from these files std::vector<String> queries_files; /// If not empty, queries will be read from these files
std::vector<String> interleave_queries_files; /// If not empty, run queries from these files before processing every file from 'queries_files'. std::vector<String> interleave_queries_files; /// If not empty, run queries from these files before processing every file from 'queries_files'.
std::vector<String> cmd_options; std::vector<String> cmd_options;

View File

@ -1,7 +1,7 @@
OK OK
OK OK
1 1
FAIL OK
0 0
4 4
2 2
@ -9,8 +9,8 @@ FAIL
1 1
1 1
4 4
FAIL OK
FAIL OK
FAIL OK
FAIL OK
FAIL OK

View File

@ -1,15 +1,15 @@
OK OK
OK OK
1 1
FAIL OK
0 0
1 1
4 4
4 4
2 2
4 4
FAIL OK
FAIL OK
FAIL OK
FAIL OK
FAIL OK

View File

@ -11,7 +11,7 @@ $CLICKHOUSE_CLIENT -xyzgarbage 2>&1 | grep -q "UNRECOGNIZED_ARGUMENTS" && echo '
$CLICKHOUSE_CLIENT --xyzgarbage 2>&1 | grep -q "UNRECOGNIZED_ARGUMENTS" && echo 'OK' || echo 'FAIL' $CLICKHOUSE_CLIENT --xyzgarbage 2>&1 | grep -q "UNRECOGNIZED_ARGUMENTS" && echo 'OK' || echo 'FAIL'
cat /etc/passwd | sed 's/:/\t/g' | $CLICKHOUSE_CLIENT --query="SELECT shell, count() AS c FROM passwd GROUP BY shell ORDER BY c DESC" --external --file=- --name=passwd --structure='login String, unused String, uid UInt16, gid UInt16, comment String, home String, shell String' xyzgarbage 2>&1 | grep -q "BAD_ARGUMENTS" && echo 'OK' || echo 'FAIL' cat /etc/passwd | sed 's/:/\t/g' | $CLICKHOUSE_CLIENT --query="SELECT shell, count() AS c FROM passwd GROUP BY shell ORDER BY c DESC" --external --file=- --name=passwd --structure='login String, unused String, uid UInt16, gid UInt16, comment String, home String, shell String' xyzgarbage 2>&1 | grep -q "SYNTAX_ERROR" && echo 'OK' || echo 'FAIL'
cat /etc/passwd | sed 's/:/\t/g' | $CLICKHOUSE_CLIENT --query="SELECT shell, count() AS c FROM passwd GROUP BY shell ORDER BY c DESC" --external -xyzgarbage --file=- --name=passwd --structure='login String, unused String, uid UInt16, gid UInt16, comment String, home String, shell String' 2>&1 | grep -q "Bad arguments" && echo 'OK' || echo 'FAIL' cat /etc/passwd | sed 's/:/\t/g' | $CLICKHOUSE_CLIENT --query="SELECT shell, count() AS c FROM passwd GROUP BY shell ORDER BY c DESC" --external -xyzgarbage --file=- --name=passwd --structure='login String, unused String, uid UInt16, gid UInt16, comment String, home String, shell String' 2>&1 | grep -q "Bad arguments" && echo 'OK' || echo 'FAIL'

View File

@ -18,9 +18,6 @@ Bad arguments
Bad arguments Bad arguments
Bad arguments Bad arguments
BAD_ARGUMENTS BAD_ARGUMENTS
Bad arguments
BAD_ARGUMENTS BAD_ARGUMENTS
Bad arguments Bad arguments
Bad arguments Bad arguments
Bad arguments
Bad arguments

View File

@ -30,9 +30,6 @@ $CLICKHOUSE_LOCAL -n --multiquery "SELECT 307; SELECT 308;" 2>&1 | grep -o 'Bad
$CLICKHOUSE_LOCAL --multiquery "SELECT 309; SELECT 310;" --multiquery 2>&1 | grep -o 'Bad arguments' $CLICKHOUSE_LOCAL --multiquery "SELECT 309; SELECT 310;" --multiquery 2>&1 | grep -o 'Bad arguments'
$CLICKHOUSE_LOCAL --multiquery "SELECT 311;" --multiquery "SELECT 312;" 2>&1 | grep -o 'Bad arguments' $CLICKHOUSE_LOCAL --multiquery "SELECT 311;" --multiquery "SELECT 312;" 2>&1 | grep -o 'Bad arguments'
$CLICKHOUSE_LOCAL --multiquery "SELECT 313;" -n "SELECT 314;" 2>&1 | grep -o 'BAD_ARGUMENTS' $CLICKHOUSE_LOCAL --multiquery "SELECT 313;" -n "SELECT 314;" 2>&1 | grep -o 'BAD_ARGUMENTS'
$CLICKHOUSE_LOCAL --multiquery "SELECT 315;" --query "SELECT 316;" 2>&1 | grep -o 'Bad arguments'
$CLICKHOUSE_LOCAL -n "SELECT 320" --query "SELECT 317;" 2>&1 | grep -o 'BAD_ARGUMENTS' $CLICKHOUSE_LOCAL -n "SELECT 320" --query "SELECT 317;" 2>&1 | grep -o 'BAD_ARGUMENTS'
$CLICKHOUSE_LOCAL --query --multiquery --multiquery "SELECT 318;" 2>&1 | grep -o 'Bad arguments'
$CLICKHOUSE_LOCAL --query --multiquery "SELECT 319;" 2>&1 | grep -o 'Bad arguments'
$CLICKHOUSE_LOCAL --query -n "SELECT 400;" 2>&1 | grep -o 'Bad arguments' $CLICKHOUSE_LOCAL --query -n "SELECT 400;" 2>&1 | grep -o 'Bad arguments'
$CLICKHOUSE_LOCAL --query -n --multiquery "SELECT 401;" 2>&1 | grep -o 'Bad arguments' $CLICKHOUSE_LOCAL --query -n --multiquery "SELECT 401;" 2>&1 | grep -o 'Bad arguments'

View File

@ -0,0 +1,17 @@
101
101
202
202
Multi-statements are not allowed
Empty query
Bad arguments
Syntax error
101
101
202
202
303
303
303
Bad arguments
Syntax error

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
. "$CURDIR"/../shell_config.sh
# clickhouse-client
$CLICKHOUSE_CLIENT --query "SELECT 101" --query "SELECT 101"
$CLICKHOUSE_CLIENT --query "SELECT 202;" --query "SELECT 202;"
$CLICKHOUSE_CLIENT --query "SELECT 303" --query "SELECT 303; SELECT 303" 2>&1 | grep -o 'Multi-statements are not allowed'
$CLICKHOUSE_CLIENT --query "" --query "" 2>&1 | grep -o 'Empty query'
$CLICKHOUSE_CLIENT --query "SELECT 303" --query 2>&1 | grep -o 'Bad arguments'
$CLICKHOUSE_CLIENT --query "SELECT 303" --query "SELE" 2>&1 | grep -o 'Syntax error'
# clickhouse-local
$CLICKHOUSE_LOCAL --query "SELECT 101" --query "SELECT 101"
$CLICKHOUSE_LOCAL --query "SELECT 202;" --query "SELECT 202;"
$CLICKHOUSE_LOCAL --query "SELECT 303" --query "SELECT 303; SELECT 303" 2>&1 # behaves differently than clickhouse-client, TODO make it consistent
$CLICKHOUSE_LOCAL --query "" --query "" 2>&1 # behaves equally different than clickhouse-client TODO
$CLICKHOUSE_LOCAL --query "SELECT 303" --query 2>&1 | grep -o 'Bad arguments'
$CLICKHOUSE_LOCAL --query "SELECT 303" --query "SELE" 2>&1 | grep -o 'Syntax error'