Skip to content

Commit 768be52

Browse files
committed
refactor(cubesql): QueryRouter - remove recursion on explain planing
1 parent 5935964 commit 768be52

File tree

1 file changed

+70
-69
lines changed

1 file changed

+70
-69
lines changed

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

Lines changed: 70 additions & 69 deletions
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,22 @@ 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 { analyze, statement, verbose, .. } => self.explain_to_plan(
118+
statement,
119+
verbose,
120+
analyze
121+
).await,
122+
other => self.plan_query(&other, qtrace, span_id).await,
123+
}
124+
}
125+
126+
async fn plan_query(
111127
&self,
112128
stmt: &ast::Statement,
113129
qtrace: &mut Option<Qtrace>,
@@ -134,15 +150,6 @@ impl QueryRouter {
134150
(ast::Statement::ShowVariable { variable }, _) => {
135151
self.show_variable_to_plan(variable, span_id.clone()).await
136152
}
137-
(
138-
ast::Statement::Explain {
139-
statement,
140-
verbose,
141-
analyze,
142-
..
143-
},
144-
_,
145-
) => self.explain_to_plan(&statement, *verbose, *analyze).await,
146153
(ast::Statement::StartTransaction { .. }, DatabaseProtocol::PostgreSQL) => {
147154
// TODO: Real support
148155
Ok(QueryPlan::MetaOk(
@@ -260,68 +267,62 @@ impl QueryRouter {
260267
.await
261268
}
262269

263-
fn explain_to_plan(
270+
async fn explain_to_plan(
264271
&self,
265-
statement: &Box<ast::Statement>,
272+
statement: Box<ast::Statement>,
266273
verbose: bool,
267274
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?;
275+
) -> Result<QueryPlan, CompilationError> {
276+
// TODO span_id ?
277+
let plan = self.plan_query(&statement, &mut None, None).await?;
276278

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-
)),
279+
match plan {
280+
QueryPlan::MetaOk(_, _) | QueryPlan::MetaTabular(_, _) => Ok(QueryPlan::MetaTabular(
281+
StatusFlags::empty(),
282+
Box::new(dataframe::DataFrame::new(
283+
vec![dataframe::Column::new(
284+
"Execution Plan".to_string(),
285+
ColumnType::String,
286+
ColumnFlags::empty(),
287+
)],
288+
vec![dataframe::Row::new(vec![dataframe::TableValue::String(
289+
"This query doesnt have a plan, because it already has values for response"
290+
.to_string(),
291+
)])],
291292
)),
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-
};
293+
)),
294+
QueryPlan::DataFusionSelect(plan, context)
295+
| QueryPlan::CreateTempTable(plan, context, _, _) => {
296+
// EXPLAIN over CREATE TABLE AS shows the SELECT query plan
297+
let plan = Arc::new(plan);
298+
let schema = LogicalPlan::explain_schema();
299+
let schema = schema.to_dfschema_ref().map_err(|err| {
300+
CompilationError::internal(format!(
301+
"Unable to get DF schema for explain plan: {}",
302+
err
303+
))
304+
})?;
320305

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

327328
fn set_role_to_plan(
@@ -535,7 +536,7 @@ impl QueryRouter {
535536
));
536537
};
537538

538-
let ObjectName(ident_parts) = name;
539+
let ast::ObjectName(ident_parts) = name;
539540
let Some(table_name) = ident_parts.last() else {
540541
return Err(CompilationError::internal(
541542
"table name contains no ident parts".to_string(),
@@ -585,7 +586,7 @@ impl QueryRouter {
585586
"DROP TABLE supports dropping only one table at a time".to_string(),
586587
));
587588
}
588-
let ObjectName(ident_parts) = names.first().unwrap();
589+
let ast::ObjectName(ident_parts) = names.first().unwrap();
589590
let Some(table_name) = ident_parts.last() else {
590591
return Err(CompilationError::internal(
591592
"table name contains no ident parts".to_string(),
@@ -674,7 +675,7 @@ pub async fn convert_statement_to_cube_query(
674675
}
675676

676677
let planner = QueryRouter::new(session.state.clone(), meta, session.session_manager.clone());
677-
planner.plan(&stmt, qtrace, span_id).await
678+
planner.plan(stmt, qtrace, span_id).await
678679
}
679680

680681
pub async fn convert_sql_to_cube_query(

0 commit comments

Comments
 (0)