Skip to content

Commit ecb0c7a

Browse files
Expose DataFusion SQL explain output
1 parent 8026cc2 commit ecb0c7a

5 files changed

Lines changed: 57 additions & 23 deletions

File tree

quickwit/quickwit-df-core/proto/datafusion.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ message ExecuteSqlRequest {
6868

6969
// Optional per-request session overrides.
7070
map<string, string> properties = 2;
71+
72+
// When true, the server returns the service-level EXPLAIN output for the
73+
// final SQL statement instead of executing it. DDL statements before the
74+
// final query are still executed for side effects.
75+
bool explain = 3;
7176
}
7277

7378
message ExecuteSqlResponse {

quickwit/quickwit-df-core/src/codegen/quickwit.datafusion.rs

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quickwit/quickwit-df-core/src/grpc.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,17 @@ impl data_fusion_service_server::DataFusionService for DataFusionServiceGrpcImpl
208208
let req = request.into_inner();
209209
let service = Arc::clone(&self.service);
210210

211+
let output = if req.explain {
212+
DataFusionOutput::Explain
213+
} else {
214+
DataFusionOutput::Records
215+
};
211216
let execution = service
212-
.execute(DataFusionRequest::records(
213-
DataFusionInput::Sql(&req.sql),
214-
&req.properties,
215-
))
217+
.execute(DataFusionRequest {
218+
input: DataFusionInput::Sql(&req.sql),
219+
output,
220+
properties: &req.properties,
221+
})
216222
.await
217223
.map_err(|err| {
218224
warn!(error = %err, "DataFusion SQL execution error");

quickwit/quickwit-df-core/src/service.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -225,18 +225,18 @@ pub struct DataFusionRatioStatistics {
225225
}
226226

227227
impl DataFusionExecutionMetadata {
228-
pub fn physical_plan_display(&self) -> String {
229-
physical_plan_display(&self.physical_plan)
228+
pub async fn physical_plan_display(&self) -> String {
229+
physical_plan_display(&self.physical_plan).await
230230
}
231231

232-
pub fn physical_plan_metadata(&self) -> DataFusionPhysicalPlanMetadata {
233-
physical_plan_metadata(&self.physical_plan)
232+
pub async fn physical_plan_metadata(&self) -> DataFusionPhysicalPlanMetadata {
233+
physical_plan_metadata(&self.physical_plan).await
234234
}
235235
}
236236

237237
impl SubstraitExecutionMetadata {
238-
pub fn physical_plan_display(&self) -> String {
239-
physical_plan_display(&self.physical_plan)
238+
pub async fn physical_plan_display(&self) -> String {
239+
physical_plan_display(&self.physical_plan).await
240240
}
241241
}
242242

@@ -461,20 +461,21 @@ impl DataFusionService {
461461
execute_stream(Arc::clone(&physical_plan), ctx.task_ctx())?
462462
}
463463
DataFusionOutput::Explain => {
464-
explain_stream(&logical_plan_display, &physical_plan, None)?
464+
explain_stream(&logical_plan_display, &physical_plan, None).await?
465465
}
466466
DataFusionOutput::ExplainAnalyze => {
467467
let analyze_execution_start = Instant::now();
468468
let num_rows =
469469
execute_plan_for_metrics(Arc::clone(&physical_plan), ctx.task_ctx()).await?;
470470
analyze_execution_duration = Some(analyze_execution_start.elapsed());
471471
analyze_output_rows = Some(num_rows);
472-
let physical_plan_metadata = physical_plan_metadata(&physical_plan);
472+
let physical_plan_metadata = physical_plan_metadata(&physical_plan).await;
473473
explain_stream(
474474
&logical_plan_display,
475475
&physical_plan,
476476
Some(&physical_plan_metadata),
477-
)?
477+
)
478+
.await?
478479
}
479480
};
480481
let stream_creation_duration = stream_creation_start.elapsed();
@@ -813,7 +814,7 @@ async fn execute_plan_for_metrics(
813814
Ok(num_rows)
814815
}
815816

816-
fn explain_stream(
817+
async fn explain_stream(
817818
logical_plan: &str,
818819
physical_plan: &Arc<dyn ExecutionPlan>,
819820
physical_plan_metadata: Option<&DataFusionPhysicalPlanMetadata>,
@@ -827,7 +828,7 @@ fn explain_stream(
827828
plan_types.push("execution_statistics_json".to_string());
828829
plans.push(physical_plan_metadata.statistics.to_json_string());
829830
} else {
830-
physical_plan_display_text = physical_plan_display(physical_plan);
831+
physical_plan_display_text = physical_plan_display(physical_plan).await;
831832
plans.push(physical_plan_display_text);
832833
}
833834
let plan_type: ArrayRef = Arc::new(StringArray::from(plan_types));
@@ -850,10 +851,10 @@ fn explain_schema() -> SchemaRef {
850851
]))
851852
}
852853

853-
fn physical_plan_metadata(
854+
async fn physical_plan_metadata(
854855
physical_plan: &Arc<dyn ExecutionPlan>,
855856
) -> DataFusionPhysicalPlanMetadata {
856-
let (physical_plan, show_metrics) = physical_plan_with_metrics(physical_plan);
857+
let (physical_plan, show_metrics) = physical_plan_with_metrics(physical_plan).await;
857858
let display = if physical_plan.as_any().is::<DistributedExec>() {
858859
display_plan_ascii(physical_plan.as_ref(), show_metrics)
859860
} else {
@@ -868,18 +869,20 @@ fn physical_plan_metadata(
868869
}
869870
}
870871

871-
fn physical_plan_display(physical_plan: &Arc<dyn ExecutionPlan>) -> String {
872-
physical_plan_metadata(physical_plan).display
872+
async fn physical_plan_display(physical_plan: &Arc<dyn ExecutionPlan>) -> String {
873+
physical_plan_metadata(physical_plan).await.display
873874
}
874875

875-
fn physical_plan_with_metrics(
876+
async fn physical_plan_with_metrics(
876877
physical_plan: &Arc<dyn ExecutionPlan>,
877878
) -> (Arc<dyn ExecutionPlan>, bool) {
878879
if physical_plan.as_any().is::<DistributedExec>() {
879880
return match rewrite_distributed_plan_with_metrics(
880881
Arc::clone(physical_plan),
881882
DistributedMetricsFormat::PerTask,
882-
) {
883+
)
884+
.await
885+
{
883886
Ok(physical_plan) => (physical_plan, true),
884887
Err(_) => (Arc::clone(physical_plan), false),
885888
};

quickwit/quickwit-integration-tests/src/tests/metrics_distributed_tests.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,26 @@ fn datafusion_client(
7979
async fn execute_datafusion_sql(
8080
client: &mut DataFusionServiceClient<tonic::transport::Channel>,
8181
sql: impl Into<String>,
82+
) -> Vec<RecordBatch> {
83+
execute_datafusion_sql_with_explain(client, sql, false).await
84+
}
85+
86+
async fn explain_datafusion_sql(
87+
client: &mut DataFusionServiceClient<tonic::transport::Channel>,
88+
sql: impl Into<String>,
89+
) -> Vec<RecordBatch> {
90+
execute_datafusion_sql_with_explain(client, sql, true).await
91+
}
92+
93+
async fn execute_datafusion_sql_with_explain(
94+
client: &mut DataFusionServiceClient<tonic::transport::Channel>,
95+
sql: impl Into<String>,
96+
explain: bool,
8297
) -> Vec<RecordBatch> {
8398
let request = ExecuteSqlRequest {
8499
sql: sql.into(),
85100
properties: Default::default(),
101+
explain,
86102
};
87103
let mut stream = client.execute_sql(request).await.unwrap().into_inner();
88104
let mut batches = Vec::new();
@@ -234,8 +250,7 @@ async fn test_distributed_tasks_not_shuffles() {
234250
let query = r#"SELECT SUM(value) as total, COUNT(*) as cnt FROM "dist-test""#;
235251

236252
let mut client = datafusion_client(&sandbox);
237-
let explain_batches =
238-
execute_datafusion_sql(&mut client, format!("{ddl}; EXPLAIN VERBOSE {query}")).await;
253+
let explain_batches = explain_datafusion_sql(&mut client, format!("{ddl}; {query}")).await;
239254
let plan_str = explain_plan_text(&explain_batches);
240255
println!("=== DataFusion EXPLAIN output ===\n{plan_str}");
241256

0 commit comments

Comments
 (0)