Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support explain decorrelated plan #16681

Merged
merged 1 commit into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Loading