Skip to content

Commit f4b03f2

Browse files
authored
refactor(cubesql): QueryRouter - remove recursion on explain planing (#8820)
1 parent 5935964 commit f4b03f2

File tree

1 file changed

+71
-69
lines changed

1 file changed

+71
-69
lines changed

rust/cubesql/cubesql/src/compile/router.rs

+71-69
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::compile::{
33
StatusFlags,
44
};
55
use sqlparser::ast;
6-
use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc, time::SystemTime};
6+
use std::{collections::HashMap, sync::Arc, time::SystemTime};
77

88
use crate::{
99
compile::{
@@ -30,7 +30,7 @@ use datafusion::{
3030
scalar::ScalarValue,
3131
};
3232
use itertools::Itertools;
33-
use sqlparser::ast::{escape_single_quote_string, ObjectName};
33+
use sqlparser::ast::escape_single_quote_string;
3434

3535
#[derive(Clone)]
3636
pub struct QueryRouter {
@@ -108,6 +108,23 @@ impl QueryRouter {
108108
}
109109

110110
pub async fn plan(
111+
&self,
112+
stmt: ast::Statement,
113+
qtrace: &mut Option<Qtrace>,
114+
span_id: Option<Arc<SpanId>>,
115+
) -> CompilationResult<QueryPlan> {
116+
match stmt {
117+
ast::Statement::Explain {
118+
analyze,
119+
statement,
120+
verbose,
121+
..
122+
} => self.explain_to_plan(statement, verbose, analyze).await,
123+
other => self.plan_query(&other, qtrace, span_id).await,
124+
}
125+
}
126+
127+
async fn plan_query(
111128
&self,
112129
stmt: &ast::Statement,
113130
qtrace: &mut Option<Qtrace>,
@@ -134,15 +151,6 @@ impl QueryRouter {
134151
(ast::Statement::ShowVariable { variable }, _) => {
135152
self.show_variable_to_plan(variable, span_id.clone()).await
136153
}
137-
(
138-
ast::Statement::Explain {
139-
statement,
140-
verbose,
141-
analyze,
142-
..
143-
},
144-
_,
145-
) => self.explain_to_plan(&statement, *verbose, *analyze).await,
146154
(ast::Statement::StartTransaction { .. }, DatabaseProtocol::PostgreSQL) => {
147155
// TODO: Real support
148156
Ok(QueryPlan::MetaOk(
@@ -260,68 +268,62 @@ impl QueryRouter {
260268
.await
261269
}
262270

263-
fn explain_to_plan(
271+
async fn explain_to_plan(
264272
&self,
265-
statement: &Box<ast::Statement>,
273+
statement: Box<ast::Statement>,
266274
verbose: bool,
267275
analyze: bool,
268-
) -> Pin<Box<dyn Future<Output = Result<QueryPlan, CompilationError>> + Send>> {
269-
let self_cloned = self.clone();
270-
271-
let statement = statement.clone();
272-
// This Boxing construct here because of recursive call to self.plan()
273-
Box::pin(async move {
274-
// TODO span_id ?
275-
let plan = self_cloned.plan(&statement, &mut None, None).await?;
276+
) -> Result<QueryPlan, CompilationError> {
277+
// TODO span_id ?
278+
let plan = self.plan_query(&statement, &mut None, None).await?;
276279

277-
match plan {
278-
QueryPlan::MetaOk(_, _) | QueryPlan::MetaTabular(_, _) => Ok(QueryPlan::MetaTabular(
279-
StatusFlags::empty(),
280-
Box::new(dataframe::DataFrame::new(
281-
vec![dataframe::Column::new(
282-
"Execution Plan".to_string(),
283-
ColumnType::String,
284-
ColumnFlags::empty(),
285-
)],
286-
vec![dataframe::Row::new(vec![dataframe::TableValue::String(
287-
"This query doesnt have a plan, because it already has values for response"
288-
.to_string(),
289-
)])],
290-
)),
280+
match plan {
281+
QueryPlan::MetaOk(_, _) | QueryPlan::MetaTabular(_, _) => Ok(QueryPlan::MetaTabular(
282+
StatusFlags::empty(),
283+
Box::new(dataframe::DataFrame::new(
284+
vec![dataframe::Column::new(
285+
"Execution Plan".to_string(),
286+
ColumnType::String,
287+
ColumnFlags::empty(),
288+
)],
289+
vec![dataframe::Row::new(vec![dataframe::TableValue::String(
290+
"This query doesnt have a plan, because it already has values for response"
291+
.to_string(),
292+
)])],
291293
)),
292-
QueryPlan::DataFusionSelect(plan, context)
293-
| QueryPlan::CreateTempTable(plan, context, _, _) => {
294-
// EXPLAIN over CREATE TABLE AS shows the SELECT query plan
295-
let plan = Arc::new(plan);
296-
let schema = LogicalPlan::explain_schema();
297-
let schema = schema.to_dfschema_ref().map_err(|err| {
298-
CompilationError::internal(format!(
299-
"Unable to get DF schema for explain plan: {}",
300-
err
301-
))
302-
})?;
303-
304-
let explain_plan = if analyze {
305-
LogicalPlan::Analyze(Analyze {
306-
verbose,
307-
input: plan,
308-
schema,
309-
})
310-
} else {
311-
let stringified_plans = vec![plan.to_stringified(PlanType::InitialLogicalPlan)];
312-
313-
LogicalPlan::Explain(Explain {
314-
verbose,
315-
plan,
316-
stringified_plans,
317-
schema,
318-
})
319-
};
294+
)),
295+
QueryPlan::DataFusionSelect(plan, context)
296+
| QueryPlan::CreateTempTable(plan, context, _, _) => {
297+
// EXPLAIN over CREATE TABLE AS shows the SELECT query plan
298+
let plan = Arc::new(plan);
299+
let schema = LogicalPlan::explain_schema();
300+
let schema = schema.to_dfschema_ref().map_err(|err| {
301+
CompilationError::internal(format!(
302+
"Unable to get DF schema for explain plan: {}",
303+
err
304+
))
305+
})?;
320306

321-
Ok(QueryPlan::DataFusionSelect(explain_plan, context))
322-
}
307+
let explain_plan = if analyze {
308+
LogicalPlan::Analyze(Analyze {
309+
verbose,
310+
input: plan,
311+
schema,
312+
})
313+
} else {
314+
let stringified_plans = vec![plan.to_stringified(PlanType::InitialLogicalPlan)];
315+
316+
LogicalPlan::Explain(Explain {
317+
verbose,
318+
plan,
319+
stringified_plans,
320+
schema,
321+
})
322+
};
323+
324+
Ok(QueryPlan::DataFusionSelect(explain_plan, context))
323325
}
324-
})
326+
}
325327
}
326328

327329
fn set_role_to_plan(
@@ -535,7 +537,7 @@ impl QueryRouter {
535537
));
536538
};
537539

538-
let ObjectName(ident_parts) = name;
540+
let ast::ObjectName(ident_parts) = name;
539541
let Some(table_name) = ident_parts.last() else {
540542
return Err(CompilationError::internal(
541543
"table name contains no ident parts".to_string(),
@@ -585,7 +587,7 @@ impl QueryRouter {
585587
"DROP TABLE supports dropping only one table at a time".to_string(),
586588
));
587589
}
588-
let ObjectName(ident_parts) = names.first().unwrap();
590+
let ast::ObjectName(ident_parts) = names.first().unwrap();
589591
let Some(table_name) = ident_parts.last() else {
590592
return Err(CompilationError::internal(
591593
"table name contains no ident parts".to_string(),
@@ -674,7 +676,7 @@ pub async fn convert_statement_to_cube_query(
674676
}
675677

676678
let planner = QueryRouter::new(session.state.clone(), meta, session.session_manager.clone());
677-
planner.plan(&stmt, qtrace, span_id).await
679+
planner.plan(stmt, qtrace, span_id).await
678680
}
679681

680682
pub async fn convert_sql_to_cube_query(

0 commit comments

Comments
 (0)