1use std::any::Any;
21use std::sync::Arc;
22use std::task::{Context, Poll};
23
24use super::work_table::{ReservedBatches, WorkTable};
25use crate::aggregates::group_values::{GroupValues, new_group_values};
26use crate::aggregates::order::GroupOrdering;
27use crate::common::project_plan_to_schema;
28use crate::execution_plan::{Boundedness, EmissionType, reset_plan_states};
29use crate::metrics::{
30 BaselineMetrics, ExecutionPlanMetricsSet, MetricsSet, RecordOutput,
31};
32use crate::{
33 ChildrenPropertiesMode, DisplayAs, DisplayFormatType, ExecutionPlan, PlanProperties,
34 RecordBatchStream, ReplaceChildrenOptions, SendableRecordBatchStream,
35};
36use arrow::array::{BooleanArray, BooleanBuilder};
37use arrow::compute::filter_record_batch;
38use arrow::datatypes::SchemaRef;
39use arrow::record_batch::RecordBatch;
40use datafusion_common::tree_node::TreeNodeRecursion;
41use datafusion_common::tree_node::{Transformed, TransformedResult, TreeNode};
42use datafusion_common::{
43 Result, exec_datafusion_err, internal_datafusion_err, not_impl_err,
44};
45use datafusion_execution::TaskContext;
46use datafusion_execution::memory_pool::{MemoryConsumer, MemoryReservation};
47use datafusion_physical_expr::PhysicalExpr;
48use datafusion_physical_expr::{EquivalenceProperties, Partitioning};
49
50use futures::{Stream, StreamExt, ready};
51
52#[derive(Debug, Clone)]
68pub struct RecursiveQueryExec {
69 name: String,
71 work_table: Arc<WorkTable>,
73 static_term: Arc<dyn ExecutionPlan>,
75 recursive_term: Arc<dyn ExecutionPlan>,
77 is_distinct: bool,
79 metrics: ExecutionPlanMetricsSet,
81 cache: Arc<PlanProperties>,
83}
84
85impl RecursiveQueryExec {
86 pub fn try_new(
88 name: String,
89 output_schema: SchemaRef,
90 static_term: Arc<dyn ExecutionPlan>,
91 recursive_term: Arc<dyn ExecutionPlan>,
92 is_distinct: bool,
93 ) -> Result<Self> {
94 let work_table = Arc::new(WorkTable::new(name.clone()));
96 let static_term = project_plan_to_schema(static_term, &output_schema)?;
98 let recursive_term = assign_work_table(recursive_term, &work_table)?;
99 let recursive_term = project_plan_to_schema(recursive_term, &output_schema)?;
100 let cache = Self::compute_properties(output_schema);
101 Ok(RecursiveQueryExec {
102 name,
103 static_term,
104 recursive_term,
105 is_distinct,
106 work_table,
107 metrics: ExecutionPlanMetricsSet::new(),
108 cache: Arc::new(cache),
109 })
110 }
111
112 pub fn name(&self) -> &str {
114 &self.name
115 }
116
117 pub fn static_term(&self) -> &Arc<dyn ExecutionPlan> {
119 &self.static_term
120 }
121
122 pub fn recursive_term(&self) -> &Arc<dyn ExecutionPlan> {
124 &self.recursive_term
125 }
126
127 pub fn is_distinct(&self) -> bool {
129 self.is_distinct
130 }
131
132 fn compute_properties(schema: SchemaRef) -> PlanProperties {
134 let eq_properties = EquivalenceProperties::new(schema);
135
136 PlanProperties::new(
137 eq_properties,
138 Partitioning::UnknownPartitioning(1),
139 EmissionType::Incremental,
140 Boundedness::Bounded,
141 )
142 }
143}
144
145impl ExecutionPlan for RecursiveQueryExec {
146 fn name(&self) -> &'static str {
147 "RecursiveQueryExec"
148 }
149
150 fn properties(&self) -> &Arc<PlanProperties> {
151 &self.cache
152 }
153
154 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
155 vec![&self.static_term, &self.recursive_term]
156 }
157
158 fn apply_expressions(
159 &self,
160 _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
161 ) -> Result<TreeNodeRecursion> {
162 Ok(TreeNodeRecursion::Continue)
163 }
164
165 fn maintains_input_order(&self) -> Vec<bool> {
168 vec![false, false]
169 }
170
171 fn benefits_from_input_partitioning(&self) -> Vec<bool> {
172 vec![false, false]
173 }
174
175 fn required_input_distribution(&self) -> Vec<crate::Distribution> {
176 self.input_distribution_requirements().into_per_child()
177 }
178
179 fn input_distribution_requirements(&self) -> crate::InputDistributionRequirements {
180 crate::InputDistributionRequirements::new(vec![
181 crate::Distribution::SinglePartition,
182 crate::Distribution::SinglePartition,
183 ])
184 }
185
186 fn replace_children(
187 self: Arc<Self>,
188 children: Vec<Arc<dyn ExecutionPlan>>,
189 _: ReplaceChildrenOptions,
190 ) -> Result<Arc<dyn ExecutionPlan>> {
191 RecursiveQueryExec::try_new(
192 self.name.clone(),
193 self.schema(),
194 Arc::clone(&children[0]),
195 Arc::clone(&children[1]),
196 self.is_distinct,
197 )
198 .map(|e| Arc::new(e) as _)
199 }
200
201 fn with_new_children(
202 self: Arc<Self>,
203 children: Vec<Arc<dyn ExecutionPlan>>,
204 ) -> Result<Arc<dyn ExecutionPlan>> {
205 self.replace_children(
206 children,
207 ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
208 )
209 }
210
211 fn execute(
212 &self,
213 partition: usize,
214 context: Arc<TaskContext>,
215 ) -> Result<SendableRecordBatchStream> {
216 if partition != 0 {
218 return Err(internal_datafusion_err!(
219 "RecursiveQueryExec got an invalid partition {partition} (expected 0)"
220 ));
221 }
222
223 let static_stream = self.static_term.execute(partition, Arc::clone(&context))?;
224 let baseline_metrics = BaselineMetrics::new(&self.metrics, partition);
225 Ok(Box::pin(RecursiveQueryStream::new(
226 context,
227 Arc::clone(&self.work_table),
228 Arc::clone(&self.recursive_term),
229 static_stream,
230 self.is_distinct,
231 baseline_metrics,
232 )?))
233 }
234
235 fn metrics(&self) -> Option<MetricsSet> {
236 Some(self.metrics.clone_inner())
237 }
238}
239
240impl DisplayAs for RecursiveQueryExec {
241 fn fmt_as(
242 &self,
243 t: DisplayFormatType,
244 f: &mut std::fmt::Formatter,
245 ) -> std::fmt::Result {
246 match t {
247 DisplayFormatType::Default | DisplayFormatType::Verbose => {
248 write!(
249 f,
250 "RecursiveQueryExec: name={}, is_distinct={}",
251 self.name, self.is_distinct
252 )
253 }
254 DisplayFormatType::TreeRender => {
255 write!(f, "")
257 }
258 }
259 }
260}
261
262struct RecursiveQueryStream {
280 task_context: Arc<TaskContext>,
282 work_table: Arc<WorkTable>,
284 recursive_term: Arc<dyn ExecutionPlan>,
286 static_stream: Option<SendableRecordBatchStream>,
289 recursive_stream: Option<SendableRecordBatchStream>,
292 schema: SchemaRef,
294 buffer: Vec<RecordBatch>,
297 reservation: MemoryReservation,
299 distinct_deduplicator: Option<DistinctDeduplicator>,
301 baseline_metrics: BaselineMetrics,
303}
304
305impl RecursiveQueryStream {
306 fn new(
308 task_context: Arc<TaskContext>,
309 work_table: Arc<WorkTable>,
310 recursive_term: Arc<dyn ExecutionPlan>,
311 static_stream: SendableRecordBatchStream,
312 is_distinct: bool,
313 baseline_metrics: BaselineMetrics,
314 ) -> Result<Self> {
315 let schema = static_stream.schema();
316 let reservation =
317 MemoryConsumer::new("RecursiveQuery").register(task_context.memory_pool());
318 let distinct_deduplicator = is_distinct
319 .then(|| DistinctDeduplicator::new(Arc::clone(&schema), &task_context))
320 .transpose()?;
321 Ok(Self {
322 task_context,
323 work_table,
324 recursive_term,
325 static_stream: Some(static_stream),
326 recursive_stream: None,
327 schema,
328 buffer: vec![],
329 reservation,
330 distinct_deduplicator,
331 baseline_metrics,
332 })
333 }
334
335 fn push_batch(
338 mut self: std::pin::Pin<&mut Self>,
339 mut batch: RecordBatch,
340 ) -> Poll<Option<Result<RecordBatch>>> {
341 let baseline_metrics = self.baseline_metrics.clone();
342
343 if let Some(deduplicator) = &mut self.distinct_deduplicator {
344 let _timer_guard = baseline_metrics.elapsed_compute().timer();
345 batch = deduplicator.deduplicate(&batch)?;
346 }
347
348 if let Err(e) = self.reservation.try_grow(batch.get_array_memory_size()) {
349 return Poll::Ready(Some(Err(e)));
350 }
351 self.buffer.push(batch.clone());
352 (&batch).record_output(&baseline_metrics);
353 Poll::Ready(Some(Ok(batch)))
354 }
355
356 fn poll_next_iteration(
360 mut self: std::pin::Pin<&mut Self>,
361 cx: &mut Context<'_>,
362 ) -> Poll<Option<Result<RecordBatch>>> {
363 let total_length = self
364 .buffer
365 .iter()
366 .fold(0, |acc, batch| acc + batch.num_rows());
367
368 if total_length == 0 {
369 return Poll::Ready(None);
370 }
371
372 let reserved_batches = ReservedBatches::new(
374 std::mem::take(&mut self.buffer),
375 self.reservation.take(),
376 );
377 self.work_table.update(reserved_batches);
378
379 let partition = 0;
382
383 let recursive_plan = reset_plan_states(Arc::clone(&self.recursive_term))?;
384 self.recursive_stream =
385 Some(recursive_plan.execute(partition, Arc::clone(&self.task_context))?);
386 self.poll_next(cx)
387 }
388}
389
390fn assign_work_table(
391 plan: Arc<dyn ExecutionPlan>,
392 work_table: &Arc<WorkTable>,
393) -> Result<Arc<dyn ExecutionPlan>> {
394 let mut work_table_refs = 0;
395 plan.transform_down(|plan| {
396 if let Some(new_plan) =
397 plan.with_new_state(Arc::clone(work_table) as Arc<dyn Any + Send + Sync>)
398 {
399 if work_table_refs > 0 {
400 not_impl_err!(
401 "Multiple recursive references to the same CTE are not supported"
402 )
403 } else {
404 work_table_refs += 1;
405 Ok(Transformed::yes(new_plan))
406 }
407 } else {
408 Ok(Transformed::no(plan))
409 }
410 })
411 .data()
412}
413
414impl Stream for RecursiveQueryStream {
415 type Item = Result<RecordBatch>;
416
417 fn poll_next(
418 mut self: std::pin::Pin<&mut Self>,
419 cx: &mut Context<'_>,
420 ) -> Poll<Option<Self::Item>> {
421 if let Some(static_stream) = &mut self.static_stream {
422 let batch_result = ready!(static_stream.poll_next_unpin(cx));
425 match &batch_result {
426 None => {
427 self.static_stream = None;
429 self.poll_next_iteration(cx)
430 }
431 Some(Ok(batch)) => self.push_batch(batch.clone()),
432 _ => Poll::Ready(batch_result),
433 }
434 } else if let Some(recursive_stream) = &mut self.recursive_stream {
435 let batch_result = ready!(recursive_stream.poll_next_unpin(cx));
436 match batch_result {
437 None => {
438 self.recursive_stream = None;
439 self.poll_next_iteration(cx)
440 }
441 Some(Ok(batch)) => self.push_batch(batch),
442 _ => Poll::Ready(batch_result),
443 }
444 } else {
445 Poll::Ready(None)
446 }
447 }
448}
449
450impl RecordBatchStream for RecursiveQueryStream {
451 fn schema(&self) -> SchemaRef {
453 Arc::clone(&self.schema)
454 }
455}
456
457struct DistinctDeduplicator {
459 group_values: Box<dyn GroupValues>,
461 reservation: MemoryReservation,
462 intern_output_buffer: Vec<usize>,
463}
464
465impl DistinctDeduplicator {
466 fn new(schema: SchemaRef, task_context: &TaskContext) -> Result<Self> {
467 let group_values = new_group_values(schema, &GroupOrdering::None)?;
468 let reservation = MemoryConsumer::new("RecursiveQueryHashTable")
469 .register(task_context.memory_pool());
470 Ok(Self {
471 group_values,
472 reservation,
473 intern_output_buffer: Vec::new(),
474 })
475 }
476
477 fn deduplicate(&mut self, batch: &RecordBatch) -> Result<RecordBatch> {
484 let size_before = self.group_values.len();
485 let additional = batch.num_rows();
486 self.intern_output_buffer
487 .try_reserve(additional)
488 .map_err(|e| {
489 exec_datafusion_err!(
490 "failed to reserve {additional} recursive query group ids: {e}"
491 )
492 })?;
493 self.group_values
494 .intern(batch.columns(), &mut self.intern_output_buffer)?;
495 let mask = new_groups_mask(&self.intern_output_buffer, size_before);
496 self.intern_output_buffer.clear();
497 self.reservation.try_resize(self.group_values.size())?;
499 Ok(filter_record_batch(batch, &mask)?)
500 }
501}
502
503fn new_groups_mask(
505 values: &[usize],
506 mut max_already_seen_group_id: usize,
507) -> BooleanArray {
508 let mut output = BooleanBuilder::with_capacity(values.len());
509 for value in values {
510 if *value >= max_already_seen_group_id {
511 output.append_value(true);
512 max_already_seen_group_id = *value + 1; } else {
514 output.append_value(false);
515 }
516 }
517 output.finish()
518}
519
520#[cfg(test)]
521mod tests {
522 use super::*;
523 use crate::empty::EmptyExec;
524 use crate::projection::ProjectionExec;
525
526 use arrow::datatypes::{DataType, Field, Schema};
527
528 fn empty_exec(fields: Vec<Field>) -> Arc<dyn ExecutionPlan> {
529 Arc::new(EmptyExec::new(Arc::new(Schema::new(fields))))
530 }
531
532 #[test]
533 fn recursive_query_exec_projects_recursive_term_to_reconciled_schema() -> Result<()> {
534 let static_term = empty_exec(vec![Field::new("value", DataType::Int32, false)]);
535 let recursive_term =
536 empty_exec(vec![Field::new("value + Int32(1)", DataType::Int32, false)]);
537
538 let exec = RecursiveQueryExec::try_new(
539 "numbers".to_string(),
540 static_term.schema(),
541 Arc::clone(&static_term),
542 Arc::clone(&recursive_term),
543 false,
544 )?;
545
546 assert_eq!(exec.schema(), static_term.schema());
547 let projection = exec
548 .recursive_term()
549 .downcast_ref::<ProjectionExec>()
550 .expect("recursive term should be aligned with ProjectionExec");
551 assert!(Arc::ptr_eq(projection.input(), &recursive_term));
552 assert!(!projection.schema().field(0).is_nullable());
553 assert_eq!(projection.expr()[0].alias, "value");
554 Ok(())
555 }
556
557 #[test]
558 fn recursive_query_exec_reconciles_nullability() -> Result<()> {
559 let static_term = empty_exec(vec![Field::new("value", DataType::Int32, false)]);
560 let recursive_term =
561 empty_exec(vec![Field::new("value + Int32(1)", DataType::Int32, true)]);
562 let output_schema = Arc::new(Schema::new(vec![Field::new(
563 "value",
564 DataType::Int32,
565 true,
566 )]));
567
568 let exec = RecursiveQueryExec::try_new(
569 "numbers".to_string(),
570 Arc::clone(&output_schema),
571 static_term,
572 recursive_term,
573 false,
574 )?;
575
576 assert!(exec.schema().field(0).is_nullable());
577 assert!(exec.static_term().schema().field(0).is_nullable());
578 assert!(exec.recursive_term().schema().field(0).is_nullable());
579 Ok(())
580 }
581}