Skip to content

Commit

Permalink
feat: support explain decorrelated plan (#16681)
Browse files Browse the repository at this point in the history
  • Loading branch information
xudong963 authored Oct 24, 2024
1 parent 9a56324 commit ea5d2d6
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/query/ast/src/ast/statements/explain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub enum ExplainKind {
// `EXPLAIN RAW` and `EXPLAIN OPTIMIZED` will be deprecated in the future,
// use explain options instead
Raw,
// `EXPLAIN DECORRELATED` will show the plan after subquery decorrelation
Decorrelated,
Optimized,

Plan,
Expand Down
1 change: 1 addition & 0 deletions src/query/ast/src/ast/statements/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ impl Display for Statement {
ExplainKind::Fragments => write!(f, " FRAGMENTS")?,
ExplainKind::Raw => write!(f, " RAW")?,
ExplainKind::Optimized => write!(f, " Optimized")?,
ExplainKind::Decorrelated => write!(f, " DECORRELATED")?,
ExplainKind::Plan => (),
ExplainKind::AnalyzePlan => write!(f, " ANALYZE")?,
ExplainKind::Join => write!(f, " JOIN")?,
Expand Down
3 changes: 2 additions & 1 deletion src/query/ast/src/parser/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub enum CreateDatabaseOption {
pub fn statement_body(i: Input) -> IResult<Statement> {
let explain = map_res(
rule! {
EXPLAIN ~ ( "(" ~ #comma_separated_list1(explain_option) ~ ")" )? ~ ( AST | SYNTAX | PIPELINE | JOIN | GRAPH | FRAGMENTS | RAW | OPTIMIZED | MEMO )? ~ #statement
EXPLAIN ~ ( "(" ~ #comma_separated_list1(explain_option) ~ ")" )? ~ ( AST | SYNTAX | PIPELINE | JOIN | GRAPH | FRAGMENTS | RAW | OPTIMIZED | MEMO | DECORRELATED)? ~ #statement
},
|(_, options, opt_kind, statement)| {
Ok(Statement::Explain {
Expand All @@ -74,6 +74,7 @@ pub fn statement_body(i: Input) -> IResult<Statement> {
Some(TokenKind::FRAGMENTS) => ExplainKind::Fragments,
Some(TokenKind::RAW) => ExplainKind::Raw,
Some(TokenKind::OPTIMIZED) => ExplainKind::Optimized,
Some(TokenKind::DECORRELATED) => ExplainKind::Decorrelated,
Some(TokenKind::MEMO) => ExplainKind::Memo("".to_string()),
Some(TokenKind::GRAPHICAL) => ExplainKind::Graphical,
None => ExplainKind::Plan,
Expand Down
2 changes: 2 additions & 0 deletions src/query/ast/src/parser/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,8 @@ pub enum TokenKind {
RAW,
#[token("OPTIMIZED", ignore(ascii_case))]
OPTIMIZED,
#[token("DECORRELATED", ignore(ascii_case))]
DECORRELATED,
#[token("SCHEMA", ignore(ascii_case))]
SCHEMA,
#[token("SCHEMAS", ignore(ascii_case))]
Expand Down
4 changes: 3 additions & 1 deletion src/query/service/src/interpreters/interpreter_explain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ impl Interpreter for ExplainInterpreter {
#[async_backtrace::framed]
async fn execute2(&self) -> Result<PipelineBuildResult> {
let blocks = match &self.kind {
ExplainKind::Raw | ExplainKind::Optimized => self.explain_plan(&self.plan)?,
ExplainKind::Raw | ExplainKind::Optimized | ExplainKind::Decorrelated => {
self.explain_plan(&self.plan)?
}
ExplainKind::Plan if self.config.logical => self.explain_plan(&self.plan)?,
ExplainKind::Plan => match &self.plan {
Plan::Query {
Expand Down
35 changes: 35 additions & 0 deletions src/query/sql/src/planner/optimizer/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,41 @@ pub async fn optimize(mut opt_ctx: OptimizerContext, plan: Plan) -> Result<Plan>
ExplainKind::Ast(_) | ExplainKind::Syntax(_) => {
Ok(Plan::Explain { config, kind, plan })
}
ExplainKind::Decorrelated => {
if let Plan::Query {
s_expr,
metadata,
bind_context,
rewrite_kind,
formatted_ast,
ignore_result,
} = *plan
{
let mut s_expr = s_expr;
if s_expr.contain_subquery() {
s_expr = Box::new(decorrelate_subquery(
opt_ctx.metadata.clone(),
*s_expr.clone(),
)?);
}
Ok(Plan::Explain {
kind,
config,
plan: Box::new(Plan::Query {
s_expr,
bind_context,
metadata,
rewrite_kind,
formatted_ast,
ignore_result,
}),
})
} else {
Err(ErrorCode::BadArguments(
"Cannot use EXPLAIN DECORRELATED with a non-query statement",
))
}
}
ExplainKind::Memo(_) => {
if let box Plan::Query { ref s_expr, .. } = plan {
let memo = get_optimized_memo(opt_ctx, *s_expr.clone()).await?;
Expand Down
67 changes: 67 additions & 0 deletions tests/sqllogictests/suites/mode/standalone/explain/subquery.test
Original file line number Diff line number Diff line change
Expand Up @@ -553,5 +553,72 @@ EvalScalar
├── push downs: [filters: [], limit: NONE]
└── estimated rows: 4.00

query T
explain optimized select i, exists(select * from t where i > 10) from t;
----
EvalScalar
├── scalars: [t.i (#0) AS (#0), exists_scalar (#4) AS (#2)]
└── Join(Cross)
├── build keys: []
├── probe keys: []
├── other filters: []
├── Scan
│ ├── table: default.t
│ ├── filters: []
│ ├── order by: []
│ └── limit: NONE
└── EvalScalar
├── scalars: [eq(count(*) (#3), 1) AS (#4)]
└── Aggregate(Final)
├── group items: []
├── aggregate functions: [count(*) (#3)]
└── Aggregate(Partial)
├── group items: []
├── aggregate functions: [count(*) (#3)]
└── Limit
├── limit: [1]
├── offset: [0]
└── Filter
├── filters: [gt(t.i (#1), 10)]
└── Scan
├── table: default.t
├── filters: [gt(t.i (#1), 10)]
├── order by: []
└── limit: NONE


query T
explain decorrelated select i, exists(select * from t where i > 10) from t;
----
EvalScalar
├── scalars: [t.i (#0) AS (#0), exists_scalar (#4) AS (#2)]
└── Join(Cross)
├── build keys: []
├── probe keys: []
├── other filters: []
├── Scan
│ ├── table: default.t
│ ├── filters: []
│ ├── order by: []
│ └── limit: NONE
└── EvalScalar
├── scalars: [eq(count(*) (#3), 1) AS (#4)]
└── Aggregate(Initial)
├── group items: []
├── aggregate functions: [count(*) (#3)]
└── Limit
├── limit: [1]
├── offset: [0]
└── EvalScalar
├── scalars: [t.i (#1) AS (#1)]
└── Filter
├── filters: [gt(t.i (#1), 10)]
└── Scan
├── table: default.t
├── filters: []
├── order by: []
└── limit: NONE


statement ok
drop table if exists t;

0 comments on commit ea5d2d6

Please sign in to comment.