use std::borrow::Borrow;
use std::collections::HashSet;
use std::sync::Arc;
use crate::error::Result;
use crate::physical_plan::coalesce_partitions::CoalescePartitionsExec;
use crate::physical_plan::limit::{GlobalLimitExec, LocalLimitExec};
use crate::physical_plan::repartition::RepartitionExec;
use crate::physical_plan::sorts::sort::SortExec;
use crate::physical_plan::sorts::sort_preserving_merge::SortPreservingMergeExec;
use crate::physical_plan::union::UnionExec;
use crate::physical_plan::windows::{BoundedWindowAggExec, WindowAggExec};
use crate::physical_plan::ExecutionPlan;
use datafusion_common::DataFusionError;
use datafusion_physical_expr::utils::ordering_satisfy;
use datafusion_physical_expr::PhysicalSortExpr;
pub fn add_sort_above(
node: &mut Arc<dyn ExecutionPlan>,
sort_expr: Vec<PhysicalSortExpr>,
) -> Result<()> {
if !ordering_satisfy(
node.output_ordering(),
Some(&sort_expr),
|| node.equivalence_properties(),
|| node.ordering_equivalence_properties(),
) {
let new_sort = SortExec::new(sort_expr, node.clone());
*node = Arc::new(if node.output_partitioning().partition_count() > 1 {
new_sort.with_preserve_partitioning(true)
} else {
new_sort
}) as _
}
Ok(())
}
pub fn find_indices<T: PartialEq, S: Borrow<T>>(
items: &[T],
targets: impl IntoIterator<Item = S>,
) -> Result<Vec<usize>> {
targets
.into_iter()
.map(|target| items.iter().position(|e| target.borrow().eq(e)))
.collect::<Option<_>>()
.ok_or_else(|| DataFusionError::Execution("Target not found".to_string()))
}
pub fn merge_and_order_indices<T: Borrow<usize>, S: Borrow<usize>>(
first: impl IntoIterator<Item = T>,
second: impl IntoIterator<Item = S>,
) -> Vec<usize> {
let mut result: Vec<_> = first
.into_iter()
.map(|e| *e.borrow())
.chain(second.into_iter().map(|e| *e.borrow()))
.collect::<HashSet<_>>()
.into_iter()
.collect();
result.sort();
result
}
pub fn is_sorted<T: Borrow<usize>>(sequence: impl IntoIterator<Item = T>) -> bool {
let mut previous = 0;
for item in sequence.into_iter() {
let current = *item.borrow();
if current < previous {
return false;
}
previous = current;
}
true
}
pub fn set_difference<T: Borrow<usize>, S: Borrow<usize>>(
first: impl IntoIterator<Item = T>,
second: impl IntoIterator<Item = S>,
) -> Vec<usize> {
let set: HashSet<_> = second.into_iter().map(|e| *e.borrow()).collect();
first
.into_iter()
.map(|e| *e.borrow())
.filter(|e| !set.contains(e))
.collect()
}
pub fn is_limit(plan: &Arc<dyn ExecutionPlan>) -> bool {
plan.as_any().is::<GlobalLimitExec>() || plan.as_any().is::<LocalLimitExec>()
}
pub fn is_window(plan: &Arc<dyn ExecutionPlan>) -> bool {
plan.as_any().is::<WindowAggExec>() || plan.as_any().is::<BoundedWindowAggExec>()
}
pub fn is_sort(plan: &Arc<dyn ExecutionPlan>) -> bool {
plan.as_any().is::<SortExec>()
}
pub fn is_sort_preserving_merge(plan: &Arc<dyn ExecutionPlan>) -> bool {
plan.as_any().is::<SortPreservingMergeExec>()
}
pub fn is_coalesce_partitions(plan: &Arc<dyn ExecutionPlan>) -> bool {
plan.as_any().is::<CoalescePartitionsExec>()
}
pub fn is_union(plan: &Arc<dyn ExecutionPlan>) -> bool {
plan.as_any().is::<UnionExec>()
}
pub fn is_repartition(plan: &Arc<dyn ExecutionPlan>) -> bool {
plan.as_any().is::<RepartitionExec>()
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_find_indices() -> Result<()> {
assert_eq!(find_indices(&[0, 3, 4], [0, 3, 4])?, vec![0, 1, 2]);
assert_eq!(find_indices(&[0, 3, 4], [0, 4, 3])?, vec![0, 2, 1]);
assert_eq!(find_indices(&[3, 0, 4], [0, 3])?, vec![1, 0]);
assert!(find_indices(&[0, 3], [0, 3, 4]).is_err());
assert!(find_indices(&[0, 3, 4], [0, 2]).is_err());
Ok(())
}
#[tokio::test]
async fn test_merge_and_order_indices() {
assert_eq!(
merge_and_order_indices([0, 3, 4], [1, 3, 5]),
vec![0, 1, 3, 4, 5]
);
assert_eq!(
merge_and_order_indices([3, 0, 4], [5, 1, 3]),
vec![0, 1, 3, 4, 5]
);
}
#[tokio::test]
async fn test_is_sorted() {
assert!(is_sorted::<usize>([]));
assert!(is_sorted([0]));
assert!(is_sorted([0, 3, 4]));
assert!(is_sorted([0, 1, 2]));
assert!(is_sorted([0, 1, 4]));
assert!(is_sorted([0usize; 0]));
assert!(is_sorted([1, 2]));
assert!(!is_sorted([3, 2]));
}
#[tokio::test]
async fn test_set_difference() {
assert_eq!(set_difference([0, 3, 4], [1, 2]), vec![0, 3, 4]);
assert_eq!(set_difference([0, 3, 4], [1, 2, 4]), vec![0, 3]);
assert_eq!(set_difference([3, 4, 0], [1, 2, 4]), vec![3, 0]);
assert_eq!(set_difference([0, 3, 4], [4, 1, 2]), vec![0, 3]);
assert_eq!(set_difference([3, 4, 0], [4, 1, 2]), vec![3, 0]);
}
}