Merge pull request #44641 from ClickHouse/vdimir/view_explain_2

Function viewExplain accept SELECT and settings
This commit is contained in:
Maksim Kita 2023-01-16 13:39:53 +03:00 committed by GitHub
commit 80f6a45376
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 217 additions and 35 deletions

View File

@ -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:

View File

@ -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();
}
}; };
} }

View File

@ -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
{ {

View File

@ -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

View File

@ -6,3 +6,11 @@
1 1
1 1
1 1
1
1
1
1
1
1
1
1

View File

@ -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