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>
|
template <typename Settings>
|
||||||
ExplainSettings<Settings> checkAndGetSettings(const ASTPtr & ast_settings)
|
ExplainSettings<Settings> checkAndGetSettings(const ASTPtr & ast_settings)
|
||||||
{
|
{
|
||||||
@ -362,13 +376,12 @@ QueryPipeline InterpreterExplainQuery::executeImpl()
|
|||||||
}
|
}
|
||||||
case ASTExplainQuery::AnalyzedSyntax:
|
case ASTExplainQuery::AnalyzedSyntax:
|
||||||
{
|
{
|
||||||
if (ast.getSettings())
|
auto settings = checkAndGetSettings<QuerySyntaxSettings>(ast.getSettings());
|
||||||
throw Exception("Settings are not supported for EXPLAIN SYNTAX query.", ErrorCodes::UNKNOWN_SETTING);
|
|
||||||
|
|
||||||
ExplainAnalyzedSyntaxVisitor::Data data(getContext());
|
ExplainAnalyzedSyntaxVisitor::Data data(getContext());
|
||||||
ExplainAnalyzedSyntaxVisitor(data).visit(query);
|
ExplainAnalyzedSyntaxVisitor(data).visit(query);
|
||||||
|
|
||||||
ast.getExplainedQuery()->format(IAST::FormatSettings(buf, false));
|
ast.getExplainedQuery()->format(IAST::FormatSettings(buf, settings.oneline));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ASTExplainQuery::QueryTree:
|
case ASTExplainQuery::QueryTree:
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace ErrorCodes
|
||||||
|
{
|
||||||
|
extern const int BAD_ARGUMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
/// AST, EXPLAIN or other query with meaning of explanation query instead of execution
|
/// AST, EXPLAIN or other query with meaning of explanation query instead of execution
|
||||||
class ASTExplainQuery : public ASTQueryWithOutput
|
class ASTExplainQuery : public ASTQueryWithOutput
|
||||||
@ -23,6 +27,45 @@ public:
|
|||||||
CurrentTransaction, /// 'EXPLAIN CURRENT TRANSACTION'
|
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_) {}
|
explicit ASTExplainQuery(ExplainKind kind_) : kind(kind_) {}
|
||||||
|
|
||||||
String getID(char delim) const override { return "Explain" + (delim + toString(kind)); }
|
String getID(char delim) const override { return "Explain" + (delim + toString(kind)); }
|
||||||
@ -103,23 +146,6 @@ private:
|
|||||||
/// Used by EXPLAIN TABLE OVERRIDE
|
/// Used by EXPLAIN TABLE OVERRIDE
|
||||||
ASTPtr table_function;
|
ASTPtr table_function;
|
||||||
ASTPtr table_override;
|
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/ASTWindowDefinition.h>
|
||||||
#include <Parsers/ASTAssignment.h>
|
#include <Parsers/ASTAssignment.h>
|
||||||
#include <Parsers/ASTColumnsMatcher.h>
|
#include <Parsers/ASTColumnsMatcher.h>
|
||||||
|
#include <Parsers/ASTExplainQuery.h>
|
||||||
|
#include <Parsers/ASTSetQuery.h>
|
||||||
#include <Parsers/ASTSelectQuery.h>
|
#include <Parsers/ASTSelectQuery.h>
|
||||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||||
#include <Parsers/ExpressionListParsers.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))
|
else if (ASTPtr explain_node; explain.parse(pos, explain_node, expected))
|
||||||
{
|
{
|
||||||
/// Replace SELECT * FROM (EXPLAIN SELECT ...) with SELECT * FROM viewExplain(EXPLAIN SELECT ...)
|
const auto & explain_query = explain_node->as<const ASTExplainQuery &>();
|
||||||
result_node = buildSelectFromTableFunction(makeASTFunction("viewExplain", explain_node));
|
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
|
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
|
||||||
#include <Parsers/ASTFunction.h>
|
#include <Parsers/ASTFunction.h>
|
||||||
#include <Parsers/ASTSelectWithUnionQuery.h>
|
#include <Parsers/ASTSelectWithUnionQuery.h>
|
||||||
|
#include <Parsers/ASTSetQuery.h>
|
||||||
|
#include <Parsers/ParserSetQuery.h>
|
||||||
|
#include <Parsers/parseQuery.h>
|
||||||
#include <Parsers/queryToString.h>
|
#include <Parsers/queryToString.h>
|
||||||
#include <Storages/StorageValues.h>
|
#include <Storages/StorageValues.h>
|
||||||
#include <TableFunctions/ITableFunction.h>
|
#include <TableFunctions/ITableFunction.h>
|
||||||
@ -20,22 +23,58 @@ namespace ErrorCodes
|
|||||||
void TableFunctionExplain::parseArguments(const ASTPtr & ast_function, ContextPtr /*context*/)
|
void TableFunctionExplain::parseArguments(const ASTPtr & ast_function, ContextPtr /*context*/)
|
||||||
{
|
{
|
||||||
const auto * function = ast_function->as<ASTFunction>();
|
const auto * function = ast_function->as<ASTFunction>();
|
||||||
if (function && function->arguments && function->arguments->children.size() == 1)
|
if (!function || !function->arguments)
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||||
"Table function '{}' cannot be called directly, use `SELECT * FROM (EXPLAIN ...)` syntax", getName());
|
"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
|
ColumnsDescription TableFunctionExplain::getActualTableStructure(ContextPtr context) const
|
||||||
|
@ -6,3 +6,11 @@
|
|||||||
1
|
1
|
||||||
1
|
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() > 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 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 SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain ILIKE '%Sort%';
|
||||||
@ -15,9 +17,69 @@ SELECT * FROM (
|
|||||||
)
|
)
|
||||||
) FORMAT Null;
|
) 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;
|
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 rows > 1000 FROM (EXPLAIN ESTIMATE SELECT sum(a) FROM t1);
|
||||||
SELECT count() == 1 FROM (EXPLAIN ESTIMATE SELECT sum(a) FROM t1);
|
SELECT count() == 1 FROM (EXPLAIN ESTIMATE SELECT sum(a) FROM t1);
|
||||||
|
|
||||||
DROP TABLE IF EXISTS 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