| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| //! A custom execution plan node owned by this library. |
| //! |
| //! This is the half of an extension bundle that makes the other half |
| //! necessary. A query planner that only rearranges stock DataFusion nodes needs |
| //! no codec of its own; one that emits a node *it* defines does, because |
| //! nothing else in the process knows how to serialize it. Shipping the planner |
| //! and the codec that carries its nodes as one bundle is the normal case, and |
| //! it is why `with_extensions` installs every codec before it binds any |
| //! planner. |
| //! |
| //! The node itself is deliberately trivial — it passes its child's stream |
| //! through untouched. A real distributed engine would ship the child plan to a |
| //! remote executor here; what matters for the example is that the node exists, |
| //! that this library's planner produces it, and that only this library's codec |
| //! can encode and decode it. |
| |
| use std::fmt; |
| use std::sync::Arc; |
| |
| use datafusion::common::Result; |
| use datafusion::common::tree_node::TreeNodeRecursion; |
| use datafusion::execution::TaskContext; |
| use datafusion::physical_expr::PhysicalExpr; |
| use datafusion::physical_plan::{ |
| DisplayAs, DisplayFormatType, ExecutionPlan, PlanProperties, SendableRecordBatchStream, |
| }; |
| |
| /// Marks a subtree this library claims for remote execution. |
| #[derive(Debug)] |
| pub(crate) struct DistributedExec { |
| input: Arc<dyn ExecutionPlan>, |
| properties: Arc<PlanProperties>, |
| } |
| |
| impl DistributedExec { |
| pub(crate) fn new(input: Arc<dyn ExecutionPlan>) -> Self { |
| // The node is pass-through, so it inherits its child's properties |
| // rather than describing anything of its own. |
| let properties = Arc::clone(input.properties()); |
| Self { input, properties } |
| } |
| } |
| |
| impl DisplayAs for DistributedExec { |
| fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "DistributedExec") |
| } |
| } |
| |
| impl ExecutionPlan for DistributedExec { |
| fn name(&self) -> &str { |
| Self::static_name() |
| } |
| |
| fn properties(&self) -> &Arc<PlanProperties> { |
| &self.properties |
| } |
| |
| fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> { |
| vec![&self.input] |
| } |
| |
| fn apply_expressions( |
| &self, |
| _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>, |
| ) -> Result<TreeNodeRecursion> { |
| // Owns no physical expressions of its own; the child holds them all. |
| Ok(TreeNodeRecursion::Continue) |
| } |
| |
| fn with_new_children( |
| self: Arc<Self>, |
| mut children: Vec<Arc<dyn ExecutionPlan>>, |
| ) -> Result<Arc<dyn ExecutionPlan>> { |
| if children.len() != 1 { |
| return datafusion::common::internal_err!( |
| "DistributedExec expects exactly one child, got {}", |
| children.len() |
| ); |
| } |
| Ok(Arc::new(Self::new(children.swap_remove(0)))) |
| } |
| |
| fn execute( |
| &self, |
| partition: usize, |
| context: Arc<TaskContext>, |
| ) -> Result<SendableRecordBatchStream> { |
| self.input.execute(partition, context) |
| } |
| } |