Skip to content

Commit ea5d2d6

Browse files
authored
feat: support explain decorrelated plan (#16681)
1 parent 9a56324 commit ea5d2d6

File tree

7 files changed

+112
-2
lines changed

7 files changed

+112
-2
lines changed

src/query/ast/src/ast/statements/explain.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub enum ExplainKind {
2929
// `EXPLAIN RAW` and `EXPLAIN OPTIMIZED` will be deprecated in the future,
3030
// use explain options instead
3131
Raw,
32+
// `EXPLAIN DECORRELATED` will show the plan after subquery decorrelation
33+
Decorrelated,
3234
Optimized,
3335

3436
Plan,

src/query/ast/src/ast/statements/statement.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ impl Display for Statement {
405405
ExplainKind::Fragments => write!(f, " FRAGMENTS")?,
406406
ExplainKind::Raw => write!(f, " RAW")?,
407407
ExplainKind::Optimized => write!(f, " Optimized")?,
408+
ExplainKind::Decorrelated => write!(f, " DECORRELATED")?,
408409
ExplainKind::Plan => (),
409410
ExplainKind::AnalyzePlan => write!(f, " ANALYZE")?,
410411
ExplainKind::Join => write!(f, " JOIN")?,

src/query/ast/src/parser/statement.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub enum CreateDatabaseOption {
5656
pub fn statement_body(i: Input) -> IResult<Statement> {
5757
let explain = map_res(
5858
rule! {
59-
EXPLAIN ~ ( "(" ~ #comma_separated_list1(explain_option) ~ ")" )? ~ ( AST | SYNTAX | PIPELINE | JOIN | GRAPH | FRAGMENTS | RAW | OPTIMIZED | MEMO )? ~ #statement
59+
EXPLAIN ~ ( "(" ~ #comma_separated_list1(explain_option) ~ ")" )? ~ ( AST | SYNTAX | PIPELINE | JOIN | GRAPH | FRAGMENTS | RAW | OPTIMIZED | MEMO | DECORRELATED)? ~ #statement
6060
},
6161
|(_, options, opt_kind, statement)| {
6262
Ok(Statement::Explain {
@@ -74,6 +74,7 @@ pub fn statement_body(i: Input) -> IResult<Statement> {
7474
Some(TokenKind::FRAGMENTS) => ExplainKind::Fragments,
7575
Some(TokenKind::RAW) => ExplainKind::Raw,
7676
Some(TokenKind::OPTIMIZED) => ExplainKind::Optimized,
77+
Some(TokenKind::DECORRELATED) => ExplainKind::Decorrelated,
7778
Some(TokenKind::MEMO) => ExplainKind::Memo("".to_string()),
7879
Some(TokenKind::GRAPHICAL) => ExplainKind::Graphical,
7980
None => ExplainKind::Plan,

src/query/ast/src/parser/token.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,8 @@ pub enum TokenKind {
10141014
RAW,
10151015
#[token("OPTIMIZED", ignore(ascii_case))]
10161016
OPTIMIZED,
1017+
#[token("DECORRELATED", ignore(ascii_case))]
1018+
DECORRELATED,
10171019
#[token("SCHEMA", ignore(ascii_case))]
10181020
SCHEMA,
10191021
#[token("SCHEMAS", ignore(ascii_case))]

src/query/service/src/interpreters/interpreter_explain.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ impl Interpreter for ExplainInterpreter {
9090
#[async_backtrace::framed]
9191
async fn execute2(&self) -> Result<PipelineBuildResult> {
9292
let blocks = match &self.kind {
93-
ExplainKind::Raw | ExplainKind::Optimized => self.explain_plan(&self.plan)?,
93+
ExplainKind::Raw | ExplainKind::Optimized | ExplainKind::Decorrelated => {
94+
self.explain_plan(&self.plan)?
95+
}
9496
ExplainKind::Plan if self.config.logical => self.explain_plan(&self.plan)?,
9597
ExplainKind::Plan => match &self.plan {
9698
Plan::Query {

src/query/sql/src/planner/optimizer/optimizer.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,41 @@ pub async fn optimize(mut opt_ctx: OptimizerContext, plan: Plan) -> Result<Plan>
204204
ExplainKind::Ast(_) | ExplainKind::Syntax(_) => {
205205
Ok(Plan::Explain { config, kind, plan })
206206
}
207+
ExplainKind::Decorrelated => {
208+
if let Plan::Query {
209+
s_expr,
210+
metadata,
211+
bind_context,
212+
rewrite_kind,
213+
formatted_ast,
214+
ignore_result,
215+
} = *plan
216+
{
217+
let mut s_expr = s_expr;
218+
if s_expr.contain_subquery() {
219+
s_expr = Box::new(decorrelate_subquery(
220+
opt_ctx.metadata.clone(),
221+
*s_expr.clone(),
222+
)?);
223+
}
224+
Ok(Plan::Explain {
225+
kind,
226+
config,
227+
plan: Box::new(Plan::Query {
228+
s_expr,
229+
bind_context,
230+
metadata,
231+
rewrite_kind,
232+
formatted_ast,
233+
ignore_result,
234+
}),
235+
})
236+
} else {
237+
Err(ErrorCode::BadArguments(
238+
"Cannot use EXPLAIN DECORRELATED with a non-query statement",
239+
))
240+
}
241+
}
207242
ExplainKind::Memo(_) => {
208243
if let box Plan::Query { ref s_expr, .. } = plan {
209244
let memo = get_optimized_memo(opt_ctx, *s_expr.clone()).await?;

tests/sqllogictests/suites/mode/standalone/explain/subquery.test

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,5 +553,72 @@ EvalScalar
553553
├── push downs: [filters: [], limit: NONE]
554554
└── estimated rows: 4.00
555555

556+
query T
557+
explain optimized select i, exists(select * from t where i > 10) from t;
558+
----
559+
EvalScalar
560+
├── scalars: [t.i (#0) AS (#0), exists_scalar (#4) AS (#2)]
561+
└── Join(Cross)
562+
├── build keys: []
563+
├── probe keys: []
564+
├── other filters: []
565+
├── Scan
566+
│ ├── table: default.t
567+
│ ├── filters: []
568+
│ ├── order by: []
569+
│ └── limit: NONE
570+
└── EvalScalar
571+
├── scalars: [eq(count(*) (#3), 1) AS (#4)]
572+
└── Aggregate(Final)
573+
├── group items: []
574+
├── aggregate functions: [count(*) (#3)]
575+
└── Aggregate(Partial)
576+
├── group items: []
577+
├── aggregate functions: [count(*) (#3)]
578+
└── Limit
579+
├── limit: [1]
580+
├── offset: [0]
581+
└── Filter
582+
├── filters: [gt(t.i (#1), 10)]
583+
└── Scan
584+
├── table: default.t
585+
├── filters: [gt(t.i (#1), 10)]
586+
├── order by: []
587+
└── limit: NONE
588+
589+
590+
query T
591+
explain decorrelated select i, exists(select * from t where i > 10) from t;
592+
----
593+
EvalScalar
594+
├── scalars: [t.i (#0) AS (#0), exists_scalar (#4) AS (#2)]
595+
└── Join(Cross)
596+
├── build keys: []
597+
├── probe keys: []
598+
├── other filters: []
599+
├── Scan
600+
│ ├── table: default.t
601+
│ ├── filters: []
602+
│ ├── order by: []
603+
│ └── limit: NONE
604+
└── EvalScalar
605+
├── scalars: [eq(count(*) (#3), 1) AS (#4)]
606+
└── Aggregate(Initial)
607+
├── group items: []
608+
├── aggregate functions: [count(*) (#3)]
609+
└── Limit
610+
├── limit: [1]
611+
├── offset: [0]
612+
└── EvalScalar
613+
├── scalars: [t.i (#1) AS (#1)]
614+
└── Filter
615+
├── filters: [gt(t.i (#1), 10)]
616+
└── Scan
617+
├── table: default.t
618+
├── filters: []
619+
├── order by: []
620+
└── limit: NONE
621+
622+
556623
statement ok
557624
drop table if exists t;

0 commit comments

Comments
 (0)