Skip to content

Commit e86491d

Browse files
yuanhang zhaoJasmine-ge
yuanhang zhao
authored andcommitted
Support (EXPLAIN SELECT ...) as a subquery (ported from clickhouse #40630) (#5857) (#826)
* Support (EXPLAIN SELECT ...) as a subquery (ported from clickhouse) * minor adjustments and changes * change stateless test file to pass the tests * adjust format and add extra info * small adjustments of format
1 parent 1bf25d4 commit e86491d

9 files changed

+258
-4
lines changed

src/Parsers/ASTExplainQuery.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class ASTExplainQuery : public ASTQueryWithOutput
3030
{
3131
auto res = std::make_shared<ASTExplainQuery>(*this);
3232
res->children.clear();
33-
res->children.push_back(children[0]->clone());
33+
if (!children.empty())
34+
res->children.push_back(children[0]->clone());
3435
cloneOutputOptions(*res);
3536
return res;
3637
}

src/Parsers/ExpressionElementParsers.cpp

+66-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
#include <Parsers/ASTAssignment.h>
1212
#include <Parsers/ASTAsterisk.h>
1313
#include <Parsers/ASTColumnsMatcher.h>
14+
#include <Parsers/ASTExplainQuery.h>
15+
#include <Parsers/ASTSelectQuery.h>
16+
#include <Parsers/ASTTablesInSelectQuery.h>
1417
#include <Parsers/ASTColumnsTransformers.h>
1518
#include <Parsers/ASTExpressionList.h>
1619
#include <Parsers/ASTFunction.h>
@@ -33,6 +36,7 @@
3336

3437
#include <Parsers/ExpressionElementParsers.h>
3538
#include <Parsers/ParserCreateQuery.h>
39+
#include <Parsers/ParserExplainQuery.h>
3640

3741
#include <Parsers/queryToString.h>
3842

@@ -53,6 +57,52 @@ namespace ErrorCodes
5357
extern const int LOGICAL_ERROR;
5458
}
5559

60+
/*
61+
* Build an AST with the following structure:
62+
*
63+
* ```
64+
* SelectWithUnionQuery (children 1)
65+
* ExpressionList (children 1)
66+
* SelectQuery (children 2)
67+
* ExpressionList (children 1)
68+
* Asterisk
69+
* TablesInSelectQuery (children 1)
70+
* TablesInSelectQueryElement (children 1)
71+
* TableExpression (children 1)
72+
* Function <...>
73+
* ```
74+
*/
75+
static ASTPtr buildSelectFromTableFunction(const std::shared_ptr<ASTFunction> & ast_function)
76+
{
77+
auto result_select_query = std::make_shared<ASTSelectWithUnionQuery>();
78+
79+
{
80+
auto select_ast = std::make_shared<ASTSelectQuery>();
81+
select_ast->setExpression(ASTSelectQuery::Expression::SELECT, std::make_shared<ASTExpressionList>());
82+
select_ast->select()->children.push_back(std::make_shared<ASTAsterisk>());
83+
84+
auto list_of_selects = std::make_shared<ASTExpressionList>();
85+
list_of_selects->children.push_back(select_ast);
86+
87+
result_select_query->children.push_back(std::move(list_of_selects));
88+
result_select_query->list_of_selects = result_select_query->children.back();
89+
90+
{
91+
auto tables = std::make_shared<ASTTablesInSelectQuery>();
92+
select_ast->setExpression(ASTSelectQuery::Expression::TABLES, tables);
93+
auto tables_elem = std::make_shared<ASTTablesInSelectQueryElement>();
94+
auto table_expr = std::make_shared<ASTTableExpression>();
95+
tables->children.push_back(tables_elem);
96+
tables_elem->table_expression = table_expr;
97+
tables_elem->children.push_back(table_expr);
98+
99+
table_expr->table_function = ast_function;
100+
table_expr->children.push_back(table_expr->table_function);
101+
}
102+
}
103+
104+
return result_select_query;
105+
}
56106

57107
bool ParserArray::parseImpl(Pos & pos, ASTPtr & node, Expected & expected, [[ maybe_unused ]] bool hint)
58108
{
@@ -140,22 +190,35 @@ bool ParserParenthesisExpression::parseImpl(Pos & pos, ASTPtr & node, Expected &
140190

141191
bool ParserSubquery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected, [[ maybe_unused ]] bool hint)
142192
{
143-
ASTPtr select_node;
144193
ParserSelectWithUnionQuery select;
194+
ParserExplainQuery explain;
145195

146196
if (pos->type != TokenType::OpeningRoundBracket)
147197
return false;
148198
++pos;
149199

150-
if (!select.parse(pos, select_node, expected))
200+
ASTPtr result_node = nullptr;
201+
202+
if (ASTPtr select_node; select.parse(pos, select_node, expected))
203+
{
204+
result_node = std::move(select_node);
205+
}
206+
else if (ASTPtr explain_node; explain.parse(pos, explain_node, expected))
207+
{
208+
/// Replace SELECT * FROM (EXPLAIN SELECT ...) with SELECT * FROM viewExplain(EXPLAIN SELECT ...)
209+
result_node = buildSelectFromTableFunction(makeASTFunction("viewExplain", explain_node));
210+
}
211+
else
212+
{
151213
return false;
214+
}
152215

153216
if (pos->type != TokenType::ClosingRoundBracket)
154217
return false;
155218
++pos;
156219

157220
node = std::make_shared<ASTSubquery>();
158-
node->children.push_back(select_node);
221+
node->children.push_back(result_node);
159222
return true;
160223
}
161224

src/Parsers/ParserExplainQuery.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,19 @@ bool ParserExplainQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected
8888
{
8989
/// Nothing to parse
9090
}
91+
else if (select_only)
92+
{
93+
if (select_p.parse(pos, query, expected))
94+
explain_query->setExplainedQuery(std::move(query));
95+
else
96+
return false;
97+
}
9198
else if (select_p.parse(pos, query, expected) ||
9299
create_p.parse(pos, query, expected) ||
93100
insert_p.parse(pos, query, expected))
101+
{
94102
explain_query->setExplainedQuery(std::move(query));
103+
}
95104
else
96105
return false;
97106

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
2+
#include <Parsers/ASTFunction.h>
3+
#include <Parsers/ASTSelectWithUnionQuery.h>
4+
#include <Parsers/queryToString.h>
5+
#include <Storages/StorageValues.h>
6+
#include <TableFunctions/ITableFunction.h>
7+
#include <TableFunctions/TableFunctionFactory.h>
8+
#include <TableFunctions/TableFunctionExplain.h>
9+
#include <TableFunctions/registerTableFunctions.h>
10+
#include <Processors/Executors/PullingPipelineExecutor.h>
11+
12+
namespace DB
13+
{
14+
namespace ErrorCodes
15+
{
16+
extern const int LOGICAL_ERROR;
17+
extern const int BAD_ARGUMENTS;
18+
}
19+
20+
void TableFunctionExplain::parseArguments(const ASTPtr & ast_function, ContextPtr /*context*/)
21+
{
22+
const auto * function = ast_function->as<ASTFunction>();
23+
if (function && function->arguments && function->arguments->children.size() == 1)
24+
{
25+
const auto & query_arg = function->arguments->children[0];
26+
27+
if (!query_arg->as<ASTExplainQuery>())
28+
throw Exception(ErrorCodes::BAD_ARGUMENTS,
29+
"Table function '{}' requires a explain query argument, got '{}'",
30+
getName(), queryToString(query_arg));
31+
32+
query = query_arg;
33+
}
34+
else
35+
{
36+
throw Exception(ErrorCodes::BAD_ARGUMENTS,
37+
"Table function '{}' cannot be called directly, use `SELECT * FROM (EXPLAIN ...)` syntax", getName());
38+
}
39+
}
40+
41+
ColumnsDescription TableFunctionExplain::getActualTableStructure(ContextPtr context) const
42+
{
43+
Block sample_block = getInterpreter(context).getSampleBlock(query->as<ASTExplainQuery>()->getKind());
44+
ColumnsDescription columns_description;
45+
for (const auto & column : sample_block.getColumnsWithTypeAndName())
46+
columns_description.add(ColumnDescription(column.name, column.type));
47+
return columns_description;
48+
}
49+
50+
static Block executeMonoBlock(QueryPipeline & pipeline)
51+
{
52+
if (!pipeline.pulling())
53+
throw Exception(ErrorCodes::LOGICAL_ERROR, "Expected pulling pipeline");
54+
55+
PullingPipelineExecutor pulling_executor(pipeline);
56+
std::vector<Block> blocks;
57+
while (true)
58+
{
59+
Block block;
60+
if (pulling_executor.pull(block))
61+
blocks.push_back(std::move(block));
62+
else
63+
break;
64+
}
65+
66+
if (blocks.size() == 1)
67+
return blocks[0];
68+
69+
return concatenateBlocks(blocks);
70+
}
71+
72+
StoragePtr TableFunctionExplain::executeImpl(
73+
const ASTPtr & /*ast_function*/, ContextPtr context, const std::string & table_name, ColumnsDescription /*cached_columns*/) const
74+
{
75+
BlockIO blockio = getInterpreter(context).execute();
76+
Block block = executeMonoBlock(blockio.pipeline);
77+
78+
StorageID storage_id(getDatabaseName(), table_name);
79+
/// proton: start cannot directly make shared DataTypePtr and StorageValues::create is the only way to do it
80+
auto storage = StorageValues::create(storage_id,getActualTableStructure(context),std::move(block));
81+
/// proton: end
82+
storage->startup();
83+
return storage;
84+
}
85+
86+
InterpreterExplainQuery TableFunctionExplain::getInterpreter(ContextPtr context) const
87+
{
88+
if (!query)
89+
throw Exception(ErrorCodes::LOGICAL_ERROR, "Table function '{}' requires a explain query argument", getName());
90+
91+
return InterpreterExplainQuery(query, context);
92+
}
93+
94+
void registerTableFunctionExplain(TableFunctionFactory & factory)
95+
{
96+
factory.registerFunction<TableFunctionExplain>({R"(
97+
Returns result of EXPLAIN query.
98+
99+
The function should not be called directly but can be invoked via `SELECT * FROM (EXPLAIN <query>)`.
100+
101+
You can use this query to process the result of EXPLAIN further using SQL (e.g., in tests).
102+
103+
Example:
104+
[example:1]
105+
106+
)",
107+
{{"1", "SELECT explain FROM (EXPLAIN AST SELECT * FROM system.numbers) WHERE explain LIKE '%Asterisk%'"}}
108+
});
109+
110+
}
111+
112+
}
113+
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#include <TableFunctions/ITableFunction.h>
4+
#include <Parsers/ASTExplainQuery.h>
5+
#include <Interpreters/InterpreterExplainQuery.h>
6+
#include <base/types.h>
7+
8+
9+
namespace DB
10+
{
11+
12+
class TableFunctionExplain : public ITableFunction
13+
{
14+
public:
15+
static constexpr auto name = "viewExplain";
16+
std::string getName() const override { return name; }
17+
18+
private:
19+
StoragePtr executeImpl(const ASTPtr & ast_function, ContextPtr context, const String & table_name, ColumnsDescription cached_columns) const override;
20+
const char * getStorageTypeName() const override { return "Explain"; }
21+
22+
void parseArguments(const ASTPtr & ast_function, ContextPtr context) override;
23+
ColumnsDescription getActualTableStructure(ContextPtr context) const override;
24+
25+
InterpreterExplainQuery getInterpreter(ContextPtr context) const;
26+
27+
ASTPtr query = nullptr;
28+
};
29+
30+
31+
}
32+

src/TableFunctions/registerTableFunctions.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ void registerTableFunctions()
2828

2929
registerTableFunctionDictionary(factory);
3030

31+
registerTableFunctionExplain(factory);
32+
3133
/// proton: starts
3234
Streaming::registerTableFunctionHop(factory);
3335
Streaming::registerTableFunctionTumble(factory);

src/TableFunctions/registerTableFunctions.h

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ void registerTableFunctionView(TableFunctionFactory & factory);
2626

2727
void registerTableFunctionDictionary(TableFunctionFactory & factory);
2828

29+
void registerTableFunctionExplain(TableFunctionFactory & factory);
30+
2931
/// proton: starts
3032
namespace Streaming
3133
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
true
2+
true
3+
true
4+
true
5+
true
6+
true
7+
true
8+
true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
SELECT count() > 3 FROM (EXPLAIN PIPELINE header = 1 SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain LIKE '%Header: number uint64%';
2+
SELECT count() > 0 FROM (EXPLAIN PLAN SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain ILIKE '%Sort%';
3+
SELECT count() > 0 FROM (EXPLAIN SELECT * FROM system.numbers ORDER BY number DESC) WHERE explain ILIKE '%Sort%';
4+
SELECT count() > 0 FROM (EXPLAIN CURRENT TRANSACTION);
5+
SELECT count() == 1 FROM (EXPLAIN SYNTAX SELECT number FROM system.numbers ORDER BY number DESC) WHERE explain ILIKE 'SELECT%';
6+
SELECT trim(explain) == 'Asterisk' FROM (EXPLAIN AST SELECT * FROM system.numbers LIMIT 10) WHERE explain LIKE '%Asterisk%';
7+
8+
SELECT * FROM (
9+
EXPLAIN AST SELECT * FROM (
10+
EXPLAIN PLAN SELECT * FROM (
11+
EXPLAIN SYNTAX SELECT trim(explain) == 'Asterisk' FROM (
12+
EXPLAIN AST SELECT * FROM system.numbers LIMIT 10
13+
) WHERE explain LIKE '%Asterisk%'
14+
)
15+
)
16+
) FORMAT Null;
17+
18+
CREATE STREAM t1 ( a uint64 ) AS SELECT number AS a,now64() as _tp_time FROM system.numbers LIMIT 10000;
19+
SELECT * FROM (SELECT sleep(3) AS n) AS a JOIN (SELECT 1 + sleep(3) AS f) AS b ON a.n == b.f; --- sleep to make sure that the stream t1 finishes being created and inserted into
20+
SELECT rows > 1000 FROM (EXPLAIN ESTIMATE SELECT sum(a) FROM table(t1));
21+
SELECT count() == 1 FROM (EXPLAIN ESTIMATE SELECT sum(a) FROM table(t1));
22+
23+
DROP STREAM IF EXISTS t1;
24+

0 commit comments

Comments
 (0)