mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 23:52:03 +00:00
Merge pull request #44641 from ClickHouse/vdimir/view_explain_2
Function viewExplain accept SELECT and settings
This commit is contained in:
commit
80f6a45376
@ -288,6 +288,20 @@ struct ExplainSettings : public Settings
|
||||
}
|
||||
};
|
||||
|
||||
struct QuerySyntaxSettings
|
||||
{
|
||||
bool oneline = false;
|
||||
|
||||
constexpr static char name[] = "SYNTAX";
|
||||
|
||||
std::unordered_map<std::string, std::reference_wrapper<bool>> boolean_settings =
|
||||
{
|
||||
{"oneline", oneline},
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::reference_wrapper<Int64>> integer_settings;
|
||||
};
|
||||
|
||||
template <typename Settings>
|
||||
ExplainSettings<Settings> checkAndGetSettings(const ASTPtr & ast_settings)
|
||||
{
|
||||
@ -362,13 +376,12 @@ QueryPipeline InterpreterExplainQuery::executeImpl()
|
||||
}
|
||||
case ASTExplainQuery::AnalyzedSyntax:
|
||||
{
|
||||
if (ast.getSettings())
|
||||
throw Exception("Settings are not supported for EXPLAIN SYNTAX query.", ErrorCodes::UNKNOWN_SETTING);
|
||||
auto settings = checkAndGetSettings<QuerySyntaxSettings>(ast.getSettings());
|
||||
|
||||
ExplainAnalyzedSyntaxVisitor::Data data(getContext());
|
||||
ExplainAnalyzedSyntaxVisitor(data).visit(query);
|
||||
|
||||
ast.getExplainedQuery()->format(IAST::FormatSettings(buf, false));
|
||||
ast.getExplainedQuery()->format(IAST::FormatSettings(buf, settings.oneline));
|
||||
break;
|
||||
}
|
||||
case ASTExplainQuery::QueryTree:
|
||||
|
@ -6,6 +6,10 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
/// AST, EXPLAIN or other query with meaning of explanation query instead of execution
|
||||
class ASTExplainQuery : public ASTQueryWithOutput
|
||||
@ -23,6 +27,45 @@ public:
|
||||
CurrentTransaction, /// 'EXPLAIN CURRENT TRANSACTION'
|
||||
};
|
||||
|
||||
static String toString(ExplainKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ParsedAST: return "EXPLAIN AST";
|
||||
case AnalyzedSyntax: return "EXPLAIN SYNTAX";
|
||||
case QueryTree: return "EXPLAIN QUERY TREE";
|
||||
case QueryPlan: return "EXPLAIN";
|
||||
case QueryPipeline: return "EXPLAIN PIPELINE";
|
||||
case QueryEstimates: return "EXPLAIN ESTIMATE";
|
||||
case TableOverride: return "EXPLAIN TABLE OVERRIDE";
|
||||
case CurrentTransaction: return "EXPLAIN CURRENT TRANSACTION";
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
static ExplainKind fromString(const String & str)
|
||||
{
|
||||
if (str == "EXPLAIN AST")
|
||||
return ParsedAST;
|
||||
if (str == "EXPLAIN SYNTAX")
|
||||
return AnalyzedSyntax;
|
||||
if (str == "EXPLAIN QUERY TREE")
|
||||
return QueryTree;
|
||||
if (str == "EXPLAIN" || str == "EXPLAIN PLAN")
|
||||
return QueryPlan;
|
||||
if (str == "EXPLAIN PIPELINE")
|
||||
return QueryPipeline;
|
||||
if (str == "EXPLAIN ESTIMATE")
|
||||
return QueryEstimates;
|
||||
if (str == "EXPLAIN TABLE OVERRIDE")
|
||||
return TableOverride;
|
||||
if (str == "EXPLAIN CURRENT TRANSACTION")
|
||||
return CurrentTransaction;
|
||||
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Unknown explain kind '{}'", str);
|
||||
}
|
||||
|
||||
explicit ASTExplainQuery(ExplainKind kind_) : kind(kind_) {}
|
||||
|
||||
String getID(char delim) const override { return "Explain" + (delim + toString(kind)); }
|
||||
@ -103,23 +146,6 @@ private:
|
||||
/// Used by EXPLAIN TABLE OVERRIDE
|
||||
ASTPtr table_function;
|
||||
ASTPtr table_override;
|
||||
|
||||
static String toString(ExplainKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ParsedAST: return "EXPLAIN AST";
|
||||
case AnalyzedSyntax: return "EXPLAIN SYNTAX";
|
||||
case QueryTree: return "EXPLAIN QUERY TREE";
|
||||
case QueryPlan: return "EXPLAIN";
|
||||
case QueryPipeline: return "EXPLAIN PIPELINE";
|
||||
case QueryEstimates: return "EXPLAIN ESTIMATE";
|
||||
case TableOverride: return "EXPLAIN TABLE OVERRIDE";
|
||||
case CurrentTransaction: return "EXPLAIN CURRENT TRANSACTION";
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include <Parsers/ASTWindowDefinition.h>
|
||||
#include <Parsers/ASTAssignment.h>
|
||||
#include <Parsers/ASTColumnsMatcher.h>
|
||||
#include <Parsers/ASTExplainQuery.h>
|
||||
#include <Parsers/ASTSetQuery.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <Parsers/ExpressionListParsers.h>
|
||||
@ -116,8 +118,40 @@ bool ParserSubquery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
}
|
||||
else if (ASTPtr explain_node; explain.parse(pos, explain_node, expected))
|
||||
{
|
||||
/// Replace SELECT * FROM (EXPLAIN SELECT ...) with SELECT * FROM viewExplain(EXPLAIN SELECT ...)
|
||||
result_node = buildSelectFromTableFunction(makeASTFunction("viewExplain", explain_node));
|
||||
const auto & explain_query = explain_node->as<const ASTExplainQuery &>();
|
||||
|
||||
if (explain_query.getTableFunction() || explain_query.getTableOverride())
|
||||
throw Exception("EXPLAIN in a subquery cannot have a table function or table override", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
/// Replace subquery `(EXPLAIN <kind> <explain_settings> SELECT ...)`
|
||||
/// with `(SELECT * FROM viewExplain("<kind>", "<explain_settings>", SELECT ...))`
|
||||
|
||||
String kind_str = ASTExplainQuery::toString(explain_query.getKind());
|
||||
|
||||
String settings_str;
|
||||
if (ASTPtr settings_ast = explain_query.getSettings())
|
||||
{
|
||||
if (!settings_ast->as<ASTSetQuery>())
|
||||
throw Exception("EXPLAIN settings must be a SET query", ErrorCodes::BAD_ARGUMENTS);
|
||||
settings_str = queryToString(settings_ast);
|
||||
}
|
||||
|
||||
const ASTPtr & explained_ast = explain_query.getExplainedQuery();
|
||||
if (explained_ast)
|
||||
{
|
||||
auto view_explain = makeASTFunction("viewExplain",
|
||||
std::make_shared<ASTLiteral>(kind_str),
|
||||
std::make_shared<ASTLiteral>(settings_str),
|
||||
explained_ast);
|
||||
result_node = buildSelectFromTableFunction(view_explain);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto view_explain = makeASTFunction("viewExplain",
|
||||
std::make_shared<ASTLiteral>(kind_str),
|
||||
std::make_shared<ASTLiteral>(settings_str));
|
||||
result_node = buildSelectFromTableFunction(view_explain);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,6 +1,9 @@
|
||||
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTSelectWithUnionQuery.h>
|
||||
#include <Parsers/ASTSetQuery.h>
|
||||
#include <Parsers/ParserSetQuery.h>
|
||||
#include <Parsers/parseQuery.h>
|
||||
#include <Parsers/queryToString.h>
|
||||
#include <Storages/StorageValues.h>
|
||||
#include <TableFunctions/ITableFunction.h>
|
||||
@ -20,22 +23,58 @@ namespace ErrorCodes
|
||||
void TableFunctionExplain::parseArguments(const ASTPtr & ast_function, ContextPtr /*context*/)
|
||||
{
|
||||
const auto * function = ast_function->as<ASTFunction>();
|
||||
if (function && function->arguments && function->arguments->children.size() == 1)
|
||||
{
|
||||
const auto & query_arg = function->arguments->children[0];
|
||||
|
||||
if (!query_arg->as<ASTExplainQuery>())
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Table function '{}' requires a explain query argument, got '{}'",
|
||||
getName(), queryToString(query_arg));
|
||||
|
||||
query = query_arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!function || !function->arguments)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Table function '{}' cannot be called directly, use `SELECT * FROM (EXPLAIN ...)` syntax", getName());
|
||||
|
||||
size_t num_args = function->arguments->children.size();
|
||||
if (num_args != 2 && num_args != 3)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Table function '{}' requires 2 or 3 arguments, got {}", getName(), num_args);
|
||||
|
||||
const auto & kind_arg = function->arguments->children[0];
|
||||
const auto * kind_literal = kind_arg->as<ASTLiteral>();
|
||||
if (!kind_literal || kind_literal->value.getType() != Field::Types::String)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Table function '{}' requires a String argument for EXPLAIN kind, got '{}'",
|
||||
getName(), queryToString(kind_arg));
|
||||
|
||||
ASTExplainQuery::ExplainKind kind = ASTExplainQuery::fromString(kind_literal->value.get<String>());
|
||||
auto explain_query = std::make_shared<ASTExplainQuery>(kind);
|
||||
|
||||
const auto * settings_arg = function->arguments->children[1]->as<ASTLiteral>();
|
||||
if (!settings_arg || settings_arg->value.getType() != Field::Types::String)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Table function '{}' requires a serialized string settings argument, got '{}'",
|
||||
getName(), queryToString(function->arguments->children[1]));
|
||||
|
||||
const auto & settings_str = settings_arg->value.get<String>();
|
||||
if (!settings_str.empty())
|
||||
{
|
||||
constexpr UInt64 max_size = 4096;
|
||||
constexpr UInt64 max_depth = 16;
|
||||
|
||||
/// parse_only_internals_ = true - we don't want to parse `SET` keyword
|
||||
ParserSetQuery settings_parser(/* parse_only_internals_ = */ true);
|
||||
ASTPtr settings_ast = parseQuery(settings_parser, settings_str, max_size, max_depth);
|
||||
explain_query->setSettings(std::move(settings_ast));
|
||||
}
|
||||
|
||||
if (function->arguments->children.size() > 2)
|
||||
{
|
||||
const auto & query_arg = function->arguments->children[2];
|
||||
if (!query_arg->as<ASTSelectWithUnionQuery>())
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Table function '{}' requires a EXPLAIN SELECT query argument, got EXPLAIN '{}'",
|
||||
getName(), queryToString(query_arg));
|
||||
explain_query->setExplainedQuery(query_arg);
|
||||
}
|
||||
else if (kind != ASTExplainQuery::ExplainKind::CurrentTransaction)
|
||||
{
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Table function '{}' requires a query argument", getName());
|
||||
}
|
||||
|
||||
query = std::move(explain_query);
|
||||
}
|
||||
|
||||
ColumnsDescription TableFunctionExplain::getActualTableStructure(ContextPtr context) const
|
||||
|
@ -6,3 +6,11 @@
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
|
@ -1,3 +1,5 @@
|
||||
SET allow_experimental_analyzer = 0;
|
||||
|
||||
SELECT count() > 3 FROM (EXPLAIN PIPELINE header = 1 SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain LIKE '%Header: number UInt64%';
|
||||
SELECT count() > 0 FROM (EXPLAIN PLAN SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain ILIKE '%Sort%';
|
||||
SELECT count() > 0 FROM (EXPLAIN SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain ILIKE '%Sort%';
|
||||
@ -15,9 +17,69 @@ SELECT * FROM (
|
||||
)
|
||||
) FORMAT Null;
|
||||
|
||||
SELECT (EXPLAIN SYNTAX oneline = 1 SELECT 1) == 'SELECT 1';
|
||||
|
||||
SELECT * FROM viewExplain('', ''); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM viewExplain('EXPLAIN AST', ''); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM viewExplain('EXPLAIN AST', '', 1); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM viewExplain('EXPLAIN AST', '', ''); -- { serverError BAD_ARGUMENTS }
|
||||
|
||||
CREATE TABLE t1 ( a UInt64 ) Engine = MergeTree ORDER BY tuple() AS SELECT number AS a FROM system.numbers LIMIT 100000;
|
||||
|
||||
SELECT rows > 1000 FROM (EXPLAIN ESTIMATE SELECT sum(a) FROM t1);
|
||||
SELECT count() == 1 FROM (EXPLAIN ESTIMATE SELECT sum(a) FROM t1);
|
||||
|
||||
DROP TABLE IF EXISTS t1;
|
||||
|
||||
SET allow_experimental_analyzer = 1;
|
||||
|
||||
SELECT count() > 3 FROM (EXPLAIN PIPELINE header = 1 SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain LIKE '%Header: system.numbers.number__ UInt64%';
|
||||
SELECT count() > 0 FROM (EXPLAIN PLAN SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain ILIKE '%Sort%';
|
||||
SELECT count() > 0 FROM (EXPLAIN SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain ILIKE '%Sort%';
|
||||
SELECT count() > 0 FROM (EXPLAIN CURRENT TRANSACTION);
|
||||
SELECT count() == 1 FROM (EXPLAIN SYNTAX SELECT number FROM system.numbers ORDER BY number DESC) WHERE explain ILIKE 'SELECT%';
|
||||
|
||||
-- We have `Identifier number` instead of `Asterisk` because query argument of `viewExplain` table function was analyzed.
|
||||
-- Compare:
|
||||
-- :) EXPLAIN AST SELECT *;
|
||||
-- ┌─explain───────────────────────────┐
|
||||
-- │ SelectWithUnionQuery (children 1) │
|
||||
-- │ ExpressionList (children 1) │
|
||||
-- │ SelectQuery (children 1) │
|
||||
-- │ ExpressionList (children 1) │
|
||||
-- │ Asterisk │
|
||||
-- └───────────────────────────────────┘
|
||||
-- :) SELECT * FROM (EXPLAIN AST SELECT *);
|
||||
-- ┌─explain─────────────────────────────────────┐
|
||||
-- │ SelectWithUnionQuery (children 1) │
|
||||
-- │ ExpressionList (children 1) │
|
||||
-- │ SelectQuery (children 2) │
|
||||
-- │ ExpressionList (children 1) │
|
||||
-- │ Identifier dummy │
|
||||
-- │ TablesInSelectQuery (children 1) │
|
||||
-- │ TablesInSelectQueryElement (children 1) │
|
||||
-- │ TableExpression (children 1) │
|
||||
-- │ TableIdentifier system.one │
|
||||
-- └─────────────────────────────────────────────┘
|
||||
-- TODO: argument of `viewExplain` (and subquery in `EXAPLAN ...`) should not be analyzed.
|
||||
-- See _Support query tree in table functions_ in https://github.com/ClickHouse/ClickHouse/issues/42648
|
||||
SELECT trim(explain) == 'Identifier number' FROM (EXPLAIN AST SELECT * FROM system.numbers LIMIT 10) WHERE explain LIKE '%Identifier number%';
|
||||
|
||||
SELECT * FROM (
|
||||
EXPLAIN AST SELECT * FROM (
|
||||
EXPLAIN PLAN SELECT * FROM (
|
||||
EXPLAIN SYNTAX SELECT trim(explain) == 'Asterisk' FROM (
|
||||
EXPLAIN AST SELECT * FROM system.numbers LIMIT 10
|
||||
) WHERE explain LIKE '%Asterisk%'
|
||||
)
|
||||
)
|
||||
) FORMAT Null;
|
||||
|
||||
SELECT (EXPLAIN SYNTAX oneline = 1 SELECT 1) == 'SELECT 1 FROM system.one';
|
||||
|
||||
SELECT * FROM viewExplain('', ''); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM viewExplain('EXPLAIN AST', ''); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM viewExplain('EXPLAIN AST', '', 1); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM viewExplain('EXPLAIN AST', '', ''); -- { serverError BAD_ARGUMENTS }
|
||||
|
||||
-- EXPLAIN ESTIMATE is not supported in experimental analyzer
|
||||
|
Loading…
Reference in New Issue
Block a user