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: Implemented a new sql explain analyze graphical #16543

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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 @@ -37,6 +37,8 @@ pub enum ExplainKind {

// Explain analyze plan
AnalyzePlan,

Graphical,
}

#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
Expand Down
10 changes: 9 additions & 1 deletion src/query/ast/src/ast/statements/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub enum Statement {
ExplainAnalyze {
// if partial is true, only scan/filter/join will be shown.
partial: bool,
graphical: bool,
query: Box<Statement>,
},

Expand Down Expand Up @@ -410,12 +411,19 @@ impl Display for Statement {
ExplainKind::AnalyzePlan => write!(f, " ANALYZE")?,
ExplainKind::Join => write!(f, " JOIN")?,
ExplainKind::Memo(_) => write!(f, " MEMO")?,
ExplainKind::Graphical => write!(f, " GRAPHICAL")?,
}
write!(f, " {query}")?;
}
Statement::ExplainAnalyze { partial, query } => {
Statement::ExplainAnalyze {
partial,
graphical,
query,
} => {
if *partial {
write!(f, "EXPLAIN ANALYZE PARTIAL {query}")?;
} else if *graphical {
write!(f, "EXPLAIN ANALYZE GRAPHICAL {query}")?;
} else {
write!(f, "EXPLAIN ANALYZE {query}")?;
}
Expand Down
25 changes: 20 additions & 5 deletions src/query/ast/src/parser/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub fn statement_body(i: Input) -> IResult<Statement> {
Some(TokenKind::RAW) => ExplainKind::Raw,
Some(TokenKind::OPTIMIZED) => ExplainKind::Optimized,
Some(TokenKind::MEMO) => ExplainKind::Memo("".to_string()),
Some(TokenKind::GRAPHICAL) => ExplainKind::Graphical,
None => ExplainKind::Plan,
_ => unreachable!(),
},
Expand All @@ -85,11 +86,25 @@ pub fn statement_body(i: Input) -> IResult<Statement> {
);
let explain_analyze = map(
rule! {
EXPLAIN ~ ANALYZE ~ PARTIAL? ~ #statement
},
|(_, _, partial, statement)| Statement::ExplainAnalyze {
partial: partial.is_some(),
query: Box::new(statement.stmt),
EXPLAIN ~ ANALYZE ~ (PARTIAL|GRAPHICAL)? ~ #statement
},
|(_, _, opt_partial_or_graphical, statement)| {
let (partial, graphical) = match opt_partial_or_graphical {
Some(Token {
kind: TokenKind::PARTIAL,
..
}) => (true, false),
Some(Token {
kind: TokenKind::GRAPHICAL,
..
}) => (false, true),
_ => (false, false),
};
Statement::ExplainAnalyze {
partial,
graphical,
query: Box::new(statement.stmt),
}
},
);

Expand Down
12 changes: 12 additions & 0 deletions src/query/ast/src/parser/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ impl<'a> Tokenizer<'a> {
prev_token: None,
}
}

pub fn contains_token(query: &str, target_kind: TokenKind) -> bool {
let mut tokenizer = Tokenizer::new(query);
while let Some(Ok(token)) = tokenizer.next() {
if token.kind == target_kind {
return true;
}
}
false
}
}

impl<'a> Iterator for Tokenizer<'a> {
Expand Down Expand Up @@ -1195,6 +1205,8 @@ pub enum TokenKind {
VARIABLE,
#[token("VERBOSE", ignore(ascii_case))]
VERBOSE,
#[token("GRAPHICAL", ignore(ascii_case))]
GRAPHICAL,
#[token("VIEW", ignore(ascii_case))]
VIEW,
#[token("VIEWS", ignore(ascii_case))]
Expand Down
2 changes: 1 addition & 1 deletion src/query/ast/tests/it/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,7 @@ fn test_statement() {
r#"DROP FUNCTION binary_reverse;"#,
r#"DROP FUNCTION isnotempty;"#,
r#"
EXECUTE IMMEDIATE
EXECUTE IMMEDIATE
$$
BEGIN
LOOP
Expand Down
67 changes: 65 additions & 2 deletions src/query/service/src/interpreters/interpreter_explain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::BTreeMap;
use std::collections::HashMap;
use std::sync::Arc;

use databend_common_ast::ast::ExplainKind;
use databend_common_ast::ast::FormatTreeNode;
use databend_common_base::runtime::profile::get_statistics_desc;
use databend_common_base::runtime::profile::ProfileDesc;
use databend_common_base::runtime::profile::ProfileStatisticsName;
use databend_common_catalog::table_context::TableContext;
use databend_common_exception::ErrorCode;
use databend_common_exception::Result;
Expand All @@ -36,6 +40,8 @@ use databend_common_sql::MetadataRef;
use databend_common_storages_result_cache::gen_result_cache_key;
use databend_common_storages_result_cache::ResultCacheReader;
use databend_common_users::UserApiProvider;
use serde::Serialize;
use serde_json;

use super::InsertMultiTableInterpreter;
use super::InterpreterFactory;
Expand All @@ -60,9 +66,17 @@ pub struct ExplainInterpreter {
config: ExplainConfig,
kind: ExplainKind,
partial: bool,
graphical: bool,
plan: Plan,
}

#[derive(Serialize)]
pub struct GraphicalProfiles {
query_id: String,
profiles: Vec<PlanProfile>,
statistics_desc: Arc<BTreeMap<ProfileStatisticsName, ProfileDesc>>,
}

#[async_trait::async_trait]
impl Interpreter for ExplainInterpreter {
fn name(&self) -> &str {
Expand Down Expand Up @@ -155,7 +169,7 @@ impl Interpreter for ExplainInterpreter {
))?,
},

ExplainKind::AnalyzePlan => match &self.plan {
ExplainKind::AnalyzePlan | ExplainKind::Graphical => match &self.plan {
Plan::Query {
s_expr,
metadata,
Expand Down Expand Up @@ -259,13 +273,15 @@ impl ExplainInterpreter {
kind: ExplainKind,
config: ExplainConfig,
partial: bool,
graphical: bool,
) -> Result<Self> {
Ok(ExplainInterpreter {
ctx,
plan,
kind,
config,
partial,
graphical,
})
}

Expand Down Expand Up @@ -377,6 +393,43 @@ impl ExplainInterpreter {
Ok(vec![DataBlock::new_from_columns(vec![formatted_plan])])
}

fn graphical_profiles_to_datablocks(profiles: GraphicalProfiles) -> Vec<DataBlock> {
let mut blocks = Vec::new();

let json_string = serde_json::to_string_pretty(&profiles)
.unwrap_or_else(|_| "Failed to format profiles".to_string());

let line_split_result: Vec<&str> = json_string.lines().collect();
let formatted_block = StringType::from_data(line_split_result);
blocks.push(DataBlock::new_from_columns(vec![formatted_block]));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
blocks.push(DataBlock::new_from_columns(vec![formatted_block]));
vec![DataBlock::new_from_columns(vec![formatted_block])]


blocks
}

#[async_backtrace::framed]
async fn explain_analyze_graphical(
&self,
s_expr: &SExpr,
metadata: &MetadataRef,
required: ColumnSet,
ignore_result: bool,
) -> Result<GraphicalProfiles> {
let query_ctx = self.ctx.clone();

let mut builder = PhysicalPlanBuilder::new(metadata.clone(), self.ctx.clone(), true);
let plan = builder.build(s_expr, required).await?;
let build_res = build_query_pipeline(&self.ctx, &[], &plan, ignore_result).await?;

// Drain the data
let query_profiles = self.execute_and_get_profiles(build_res)?;

Ok(GraphicalProfiles {
query_id: query_ctx.get_id(),
profiles: query_profiles.values().cloned().collect(),
statistics_desc: get_statistics_desc(),
})
}

#[async_backtrace::framed]
async fn explain_analyze(
&self,
Expand All @@ -395,11 +448,21 @@ impl ExplainInterpreter {
let result = if self.partial {
format_partial_tree(&plan, metadata, &query_profiles)?.format_pretty()?
} else {
plan.format(metadata.clone(), query_profiles)?
plan.format(metadata.clone(), query_profiles.clone())?
.format_pretty()?
};
let line_split_result: Vec<&str> = result.lines().collect();
let formatted_plan = StringType::from_data(line_split_result);

if self.graphical {
let profiles = GraphicalProfiles {
query_id: self.ctx.clone().get_id(),
profiles: query_profiles.clone().values().cloned().collect(),
statistics_desc: get_statistics_desc(),
};
return Ok(Self::graphical_profiles_to_datablocks(profiles));
}

Ok(vec![DataBlock::new_from_columns(vec![formatted_plan])])
}

Expand Down
10 changes: 9 additions & 1 deletion src/query/service/src/interpreters/interpreter_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,27 +123,35 @@ impl InterpreterFactory {
kind.clone(),
config.clone(),
false,
false,
)?)),
Plan::ExplainAst { formatted_string } => Ok(Arc::new(ExplainInterpreter::try_create(
ctx,
plan.clone(),
ExplainKind::Ast(formatted_string.clone()),
ExplainConfig::default(),
false,
false,
)?)),
Plan::ExplainSyntax { formatted_sql } => Ok(Arc::new(ExplainInterpreter::try_create(
ctx,
plan.clone(),
ExplainKind::Syntax(formatted_sql.clone()),
ExplainConfig::default(),
false,
false,
)?)),
Plan::ExplainAnalyze { partial, plan } => Ok(Arc::new(ExplainInterpreter::try_create(
Plan::ExplainAnalyze {
graphical,
partial,
plan,
} => Ok(Arc::new(ExplainInterpreter::try_create(
ctx,
*plan.clone(),
ExplainKind::AnalyzePlan,
ExplainConfig::default(),
*partial,
*graphical,
)?)),

Plan::CopyIntoTable(copy_plan) => Ok(Arc::new(CopyIntoTableInterpreter::try_create(
Expand Down
4 changes: 2 additions & 2 deletions src/query/sql/src/planner/binder/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ impl<'a> Binder {
self.bind_explain(bind_context, kind, options, query).await?
}

Statement::ExplainAnalyze {partial, query } => {
Statement::ExplainAnalyze {partial, graphical, query } => {
let plan = self.bind_statement(bind_context, query).await?;
Plan::ExplainAnalyze { partial: *partial, plan: Box::new(plan) }
Plan::ExplainAnalyze { partial: *partial, graphical: *graphical, plan: Box::new(plan) }
}

Statement::ShowFunctions { show_options } => {
Expand Down
7 changes: 6 additions & 1 deletion src/query/sql/src/planner/optimizer/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,13 @@ pub async fn optimize(mut opt_ctx: OptimizerContext, plan: Plan) -> Result<Plan>
}
}
},
Plan::ExplainAnalyze { plan, partial } => Ok(Plan::ExplainAnalyze {
Plan::ExplainAnalyze {
plan,
partial,
graphical,
} => Ok(Plan::ExplainAnalyze {
partial,
graphical,
plan: Box::new(Box::pin(optimize(opt_ctx, *plan)).await?),
}),
Plan::CopyIntoLocation(CopyIntoLocationPlan { stage, path, from }) => {
Expand Down
1 change: 1 addition & 0 deletions src/query/sql/src/planner/plans/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ pub enum Plan {
},
ExplainAnalyze {
partial: bool,
graphical: bool,
plan: Box<Plan>,
},

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
statement ok
drop database if exists testdb

statement ok
create database testdb

statement ok
use testdb

statement ok
create table t(a int, b int)

statement ok
insert into t select number, number + 1 from numbers(1000)

query T
explain analyze graphical select * from t where a = 1

statement ok
drop database testdb
Loading