Skip to main content

datafusion_substrait/logical_plan/producer/
substrait_producer.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::extensions::Extensions;
19use crate::logical_plan::producer::{
20    from_aggregate, from_aggregate_function, from_alias, from_between, from_binary_expr,
21    from_case, from_cast, from_column, from_distinct, from_empty_relation, from_exists,
22    from_filter, from_higher_order_function, from_in_list, from_in_subquery, from_join,
23    from_lambda, from_lambda_variable, from_like, from_limit, from_literal,
24    from_placeholder, from_projection, from_repartition, from_scalar_function,
25    from_scalar_subquery, from_set_comparison, from_sort, from_subquery_alias,
26    from_table_scan, from_try_cast, from_unary_expr, from_union, from_values,
27    from_window, from_window_function, to_substrait_rel, to_substrait_rex,
28    to_substrait_type_from_field,
29};
30use datafusion::arrow::datatypes::FieldRef;
31use datafusion::common::{
32    Column, DFSchemaRef, HashMap, ScalarValue, not_impl_err, substrait_err,
33};
34use datafusion::execution::SessionState;
35use datafusion::execution::registry::SerializerRegistry;
36use datafusion::logical_expr::Subquery;
37use datafusion::logical_expr::expr::{
38    Alias, Exists, InList, InSubquery, Lambda, LambdaVariable, Placeholder,
39    SetComparison, WindowFunction,
40};
41use datafusion::logical_expr::{
42    Aggregate, Between, BinaryExpr, Case, Cast, Distinct, EmptyRelation, Expr, Extension,
43    Filter, Join, Like, Limit, LogicalPlan, Projection, Repartition, Sort, SubqueryAlias,
44    TableScan, TryCast, Union, Values, Window, expr,
45};
46use pbjson_types::Any as ProtoAny;
47use substrait::proto::aggregate_rel::Measure;
48use substrait::proto::rel::RelType;
49use substrait::proto::{
50    Expression, ExtensionLeafRel, ExtensionMultiRel, ExtensionSingleRel, Rel,
51};
52
53/// This trait is used to produce Substrait plans, converting them from DataFusion Logical Plans.
54/// It can be implemented by users to allow for custom handling of relations, expressions, etc.
55///
56/// Combined with the [crate::logical_plan::consumer::SubstraitConsumer] this allows for fully
57/// customizable Substrait serde.
58///
59/// # Example Usage
60///
61/// ```
62/// # use std::sync::Arc;
63/// # use substrait::proto::{Expression, Rel};
64/// # use substrait::proto::rel::RelType;
65/// # use datafusion::arrow::datatypes::FieldRef;
66/// # use datafusion::common::DFSchemaRef;
67/// # use datafusion::error::Result;
68/// # use datafusion::execution::SessionState;
69/// # use datafusion::logical_expr::{Between, Extension, Projection};
70/// # use datafusion_substrait::extensions::Extensions;
71/// # use datafusion_substrait::logical_plan::producer::{from_projection, SubstraitProducer, DefaultSubstraitLambdaProducer, lambda_parameters_map};
72///
73/// struct CustomSubstraitProducer {
74///     extensions: Extensions,
75///     state: Arc<SessionState>,
76///     // You can reuse existing producer code related to lambdas
77///     lambda_producer: DefaultSubstraitLambdaProducer,
78/// }
79///
80/// impl SubstraitProducer for CustomSubstraitProducer {
81///
82///     fn register_function(&mut self, signature: String) -> u32 {
83///        self.extensions.register_function(&signature)
84///     }
85///
86///     fn register_type(&mut self, type_name: String) -> u32 {
87///         self.extensions.register_type(&type_name)
88///     }
89///
90///     fn get_extensions(self) -> Extensions {
91///         self.extensions
92///     }
93///
94///    fn push_lambda_parameters(
95///        &mut self,
96///        lambda_parameters: Vec<FieldRef>,
97///    ) -> datafusion::common::Result<()> {
98///        let lambda_parameters_map = lambda_parameters_map(self, lambda_parameters)?;
99///
100///        self.lambda_producer
101///            .push_lambda_parameters(lambda_parameters_map);
102///
103///        Ok(())
104///    }
105///
106///    fn pop_lambda_parameters(&mut self) -> datafusion::common::Result<()> {
107///        self.lambda_producer.pop_lambda_parameters()
108///    }
109///
110///    fn lambda_variable(&self, name: &str) -> datafusion::common::Result<(u32, i32)> {
111///        self.lambda_producer.lambda_variable(name)
112///    }
113///
114///    fn lambda_parameter_type(
115///        &self,
116///        name: &str,
117///    ) -> datafusion::common::Result<substrait::proto::Type> {
118///        self.lambda_producer.lambda_parameter_type(name)
119///    }
120///
121///     // You can set additional metadata on the Rels you produce
122///     fn handle_projection(&mut self, plan: &Projection) -> Result<Box<Rel>> {
123///         let mut rel = from_projection(self, plan)?;
124///         match rel.rel_type {
125///             Some(RelType::Project(mut project)) => {
126///                 let mut project = project.clone();
127///                 // set common metadata or advanced extension
128///                 project.common = None;
129///                 project.advanced_extension = None;
130///                 Ok(Box::new(Rel {
131///                     rel_type: Some(RelType::Project(project)),
132///                 }))
133///             }
134///             rel_type => Ok(Box::new(Rel { rel_type })),
135///        }
136///     }
137///
138///     // You can tweak how you convert expressions for your target system
139///     fn handle_between(&mut self, between: &Between, schema: &DFSchemaRef) -> Result<Expression> {
140///        // add your own encoding for Between
141///        todo!()
142///    }
143///
144///     // You can fully control how you convert UserDefinedLogicalNodes into Substrait
145///     fn handle_extension(&mut self, _plan: &Extension) -> Result<Box<Rel>> {
146///         // implement your own serializer into Substrait
147///        todo!()
148///    }
149/// }
150/// ```
151pub trait SubstraitProducer: Send + Sync + Sized {
152    /// Within a Substrait plan, functions are referenced using function anchors that are stored at
153    /// the top level of the [Plan](substrait::proto::Plan) within
154    /// [ExtensionFunction](substrait::proto::extensions::simple_extension_declaration::ExtensionFunction)
155    /// messages.
156    ///
157    /// When given a function signature, this method should return the existing anchor for it if
158    /// there is one. Otherwise, it should generate a new anchor.
159    fn register_function(&mut self, signature: String) -> u32;
160
161    /// Within a Substrait plan, user defined types are referenced using type anchors that are stored at
162    /// the top level of the [Plan](substrait::proto::Plan) within
163    /// [ExtensionType](substrait::proto::extensions::simple_extension_declaration::ExtensionType)
164    /// messages.
165    ///
166    /// When given a type name, this method should return the existing anchor for it if
167    /// there is one. Otherwise, it should generate a new anchor.
168    fn register_type(&mut self, name: String) -> u32;
169
170    /// Consume the producer to generate the [Extensions] for the Substrait plan based on the
171    /// functions that have been registered
172    fn get_extensions(self) -> Extensions;
173
174    // Logical Plan Methods
175    // There is one method per LogicalPlan to allow for easy overriding of producer behaviour.
176    // These methods have default implementations calling the common handler code, to allow for users
177    // to re-use common handling logic.
178
179    fn handle_plan(
180        &mut self,
181        plan: &LogicalPlan,
182    ) -> datafusion::common::Result<Box<Rel>> {
183        to_substrait_rel(self, plan)
184    }
185
186    fn handle_projection(
187        &mut self,
188        plan: &Projection,
189    ) -> datafusion::common::Result<Box<Rel>> {
190        from_projection(self, plan)
191    }
192
193    fn handle_filter(&mut self, plan: &Filter) -> datafusion::common::Result<Box<Rel>> {
194        from_filter(self, plan)
195    }
196
197    fn handle_window(&mut self, plan: &Window) -> datafusion::common::Result<Box<Rel>> {
198        from_window(self, plan)
199    }
200
201    fn handle_aggregate(
202        &mut self,
203        plan: &Aggregate,
204    ) -> datafusion::common::Result<Box<Rel>> {
205        from_aggregate(self, plan)
206    }
207
208    fn handle_sort(&mut self, plan: &Sort) -> datafusion::common::Result<Box<Rel>> {
209        from_sort(self, plan)
210    }
211
212    fn handle_join(&mut self, plan: &Join) -> datafusion::common::Result<Box<Rel>> {
213        from_join(self, plan)
214    }
215
216    fn handle_repartition(
217        &mut self,
218        plan: &Repartition,
219    ) -> datafusion::common::Result<Box<Rel>> {
220        from_repartition(self, plan)
221    }
222
223    fn handle_union(&mut self, plan: &Union) -> datafusion::common::Result<Box<Rel>> {
224        from_union(self, plan)
225    }
226
227    fn handle_table_scan(
228        &mut self,
229        plan: &TableScan,
230    ) -> datafusion::common::Result<Box<Rel>> {
231        from_table_scan(self, plan)
232    }
233
234    fn handle_empty_relation(
235        &mut self,
236        plan: &EmptyRelation,
237    ) -> datafusion::common::Result<Box<Rel>> {
238        from_empty_relation(self, plan)
239    }
240
241    fn handle_subquery_alias(
242        &mut self,
243        plan: &SubqueryAlias,
244    ) -> datafusion::common::Result<Box<Rel>> {
245        from_subquery_alias(self, plan)
246    }
247
248    fn handle_limit(&mut self, plan: &Limit) -> datafusion::common::Result<Box<Rel>> {
249        from_limit(self, plan)
250    }
251
252    fn handle_values(&mut self, plan: &Values) -> datafusion::common::Result<Box<Rel>> {
253        from_values(self, plan)
254    }
255
256    fn handle_distinct(
257        &mut self,
258        plan: &Distinct,
259    ) -> datafusion::common::Result<Box<Rel>> {
260        from_distinct(self, plan)
261    }
262
263    fn handle_extension(
264        &mut self,
265        _plan: &Extension,
266    ) -> datafusion::common::Result<Box<Rel>> {
267        substrait_err!(
268            "Specify handling for LogicalPlan::Extension by implementing the SubstraitProducer trait"
269        )
270    }
271
272    // Expression Methods
273    // There is one method per DataFusion Expr to allow for easy overriding of producer behaviour
274    // These methods have default implementations calling the common handler code, to allow for users
275    // to re-use common handling logic.
276
277    fn handle_expr(
278        &mut self,
279        expr: &Expr,
280        schema: &DFSchemaRef,
281    ) -> datafusion::common::Result<Expression> {
282        to_substrait_rex(self, expr, schema)
283    }
284
285    fn handle_alias(
286        &mut self,
287        alias: &Alias,
288        schema: &DFSchemaRef,
289    ) -> datafusion::common::Result<Expression> {
290        from_alias(self, alias, schema)
291    }
292
293    fn handle_column(
294        &mut self,
295        column: &Column,
296        schema: &DFSchemaRef,
297    ) -> datafusion::common::Result<Expression> {
298        from_column(column, schema)
299    }
300
301    fn handle_literal(
302        &mut self,
303        value: &ScalarValue,
304    ) -> datafusion::common::Result<Expression> {
305        from_literal(self, value)
306    }
307
308    fn handle_binary_expr(
309        &mut self,
310        expr: &BinaryExpr,
311        schema: &DFSchemaRef,
312    ) -> datafusion::common::Result<Expression> {
313        from_binary_expr(self, expr, schema)
314    }
315
316    fn handle_like(
317        &mut self,
318        like: &Like,
319        schema: &DFSchemaRef,
320    ) -> datafusion::common::Result<Expression> {
321        from_like(self, like, schema)
322    }
323
324    /// For handling Not, IsNotNull, IsNull, IsTrue, IsFalse, IsUnknown, IsNotTrue, IsNotFalse, IsNotUnknown, Negative
325    fn handle_unary_expr(
326        &mut self,
327        expr: &Expr,
328        schema: &DFSchemaRef,
329    ) -> datafusion::common::Result<Expression> {
330        from_unary_expr(self, expr, schema)
331    }
332
333    fn handle_between(
334        &mut self,
335        between: &Between,
336        schema: &DFSchemaRef,
337    ) -> datafusion::common::Result<Expression> {
338        from_between(self, between, schema)
339    }
340
341    fn handle_case(
342        &mut self,
343        case: &Case,
344        schema: &DFSchemaRef,
345    ) -> datafusion::common::Result<Expression> {
346        from_case(self, case, schema)
347    }
348
349    fn handle_cast(
350        &mut self,
351        cast: &Cast,
352        schema: &DFSchemaRef,
353    ) -> datafusion::common::Result<Expression> {
354        from_cast(self, cast, schema)
355    }
356
357    fn handle_try_cast(
358        &mut self,
359        cast: &TryCast,
360        schema: &DFSchemaRef,
361    ) -> datafusion::common::Result<Expression> {
362        from_try_cast(self, cast, schema)
363    }
364
365    fn handle_scalar_function(
366        &mut self,
367        scalar_fn: &expr::ScalarFunction,
368        schema: &DFSchemaRef,
369    ) -> datafusion::common::Result<Expression> {
370        from_scalar_function(self, scalar_fn, schema)
371    }
372
373    fn handle_higher_order_function(
374        &mut self,
375        scalar_fn: &expr::HigherOrderFunction,
376        schema: &DFSchemaRef,
377    ) -> datafusion::common::Result<Expression> {
378        from_higher_order_function(self, scalar_fn, schema)
379    }
380
381    fn handle_aggregate_function(
382        &mut self,
383        agg_fn: &expr::AggregateFunction,
384        schema: &DFSchemaRef,
385    ) -> datafusion::common::Result<Measure> {
386        from_aggregate_function(self, agg_fn, schema)
387    }
388
389    fn handle_window_function(
390        &mut self,
391        window_fn: &WindowFunction,
392        schema: &DFSchemaRef,
393    ) -> datafusion::common::Result<Expression> {
394        from_window_function(self, window_fn, schema)
395    }
396
397    fn handle_in_list(
398        &mut self,
399        in_list: &InList,
400        schema: &DFSchemaRef,
401    ) -> datafusion::common::Result<Expression> {
402        from_in_list(self, in_list, schema)
403    }
404
405    fn handle_in_subquery(
406        &mut self,
407        in_subquery: &InSubquery,
408        schema: &DFSchemaRef,
409    ) -> datafusion::common::Result<Expression> {
410        from_in_subquery(self, in_subquery, schema)
411    }
412
413    fn handle_set_comparison(
414        &mut self,
415        set_comparison: &SetComparison,
416        schema: &DFSchemaRef,
417    ) -> datafusion::common::Result<Expression> {
418        from_set_comparison(self, set_comparison, schema)
419    }
420    fn handle_scalar_subquery(
421        &mut self,
422        subquery: &Subquery,
423        schema: &DFSchemaRef,
424    ) -> datafusion::common::Result<Expression> {
425        from_scalar_subquery(self, subquery, schema)
426    }
427
428    fn handle_exists(
429        &mut self,
430        exists: &Exists,
431        schema: &DFSchemaRef,
432    ) -> datafusion::common::Result<Expression> {
433        from_exists(self, exists, schema)
434    }
435
436    fn handle_placeholder(
437        &mut self,
438        placeholder: &Placeholder,
439        _schema: &DFSchemaRef,
440    ) -> datafusion::common::Result<Expression> {
441        from_placeholder(self, placeholder)
442    }
443
444    fn handle_lambda(
445        &mut self,
446        lambda: &Lambda,
447        schema: &DFSchemaRef,
448    ) -> datafusion::common::Result<Expression> {
449        from_lambda(self, lambda, schema)
450    }
451
452    fn handle_lambda_variable(
453        &mut self,
454        lambda_variable: &LambdaVariable,
455        schema: &DFSchemaRef,
456    ) -> datafusion::common::Result<Expression> {
457        from_lambda_variable(self, lambda_variable, schema)
458    }
459
460    // Lambda related methods
461
462    /// Push the given `lambda_parameters` into this producer so they can be referenced by lambda variables
463    ///
464    /// Note for custom implementations it's possible to embed a [DefaultSubstraitLambdaProducer] and forward this method to it
465    fn push_lambda_parameters(
466        &mut self,
467        _lambda_parameters: Vec<FieldRef>,
468    ) -> datafusion::common::Result<()> {
469        not_impl_err!("SubstraitProducer::push_lambda_parameters")
470    }
471
472    /// Pop the last pushed `lambda_parameters` so that it unshadow any previously shadowed lambda parameter
473    ///
474    /// Note for custom implementations it's possible to embed a [DefaultSubstraitLambdaProducer] and forward this method to it
475    fn pop_lambda_parameters(&mut self) -> datafusion::common::Result<()> {
476        not_impl_err!("SubstraitProducer::pop_lambda_parameters")
477    }
478
479    /// Get the (`steps_out`, `field_idx`) of the lambda variable with the given `name`. `steps_out` refers to the number
480    /// of lambda boundaries to traverse (0 = current lambda), and `field_idx` refers to the index within the lambda parameters
481    ///
482    /// Note for custom implementations it's possible to embed a [DefaultSubstraitLambdaProducer] and forward this method to it
483    fn lambda_variable(&self, _name: &str) -> datafusion::common::Result<(u32, i32)> {
484        not_impl_err!("SubstraitProducer::lambda_variable")
485    }
486
487    /// Get the type of the lambda parameter with the given `name`
488    ///
489    /// Note for custom implementations it's possible to embed a [DefaultSubstraitLambdaProducer] and forward this method to it
490    fn lambda_parameter_type(
491        &self,
492        _name: &str,
493    ) -> datafusion::common::Result<substrait::proto::Type> {
494        not_impl_err!("SubstraitProducer::lambda_parameter_type")
495    }
496}
497
498pub struct DefaultSubstraitProducer<'a> {
499    extensions: Extensions,
500    serializer_registry: &'a dyn SerializerRegistry,
501    lambda_producer: DefaultSubstraitLambdaProducer,
502}
503
504impl<'a> DefaultSubstraitProducer<'a> {
505    pub fn new(state: &'a SessionState) -> Self {
506        DefaultSubstraitProducer {
507            extensions: Extensions::default(),
508            serializer_registry: state.serializer_registry().as_ref(),
509            lambda_producer: DefaultSubstraitLambdaProducer::new(),
510        }
511    }
512}
513
514impl SubstraitProducer for DefaultSubstraitProducer<'_> {
515    fn register_function(&mut self, fn_name: String) -> u32 {
516        self.extensions.register_function(&fn_name)
517    }
518
519    fn register_type(&mut self, type_name: String) -> u32 {
520        self.extensions.register_type(&type_name)
521    }
522
523    fn get_extensions(self) -> Extensions {
524        self.extensions
525    }
526
527    fn handle_extension(
528        &mut self,
529        plan: &Extension,
530    ) -> datafusion::common::Result<Box<Rel>> {
531        let extension_bytes = self
532            .serializer_registry
533            .serialize_logical_plan(plan.node.as_ref())?;
534        let detail = ProtoAny {
535            type_url: plan.node.name().to_string(),
536            value: extension_bytes.into(),
537        };
538        let mut inputs_rel = plan
539            .node
540            .inputs()
541            .into_iter()
542            .map(|plan| self.handle_plan(plan))
543            .collect::<datafusion::common::Result<Vec<_>>>()?;
544        let rel_type = match inputs_rel.len() {
545            0 => RelType::ExtensionLeaf(ExtensionLeafRel {
546                common: None,
547                detail: Some(detail),
548            }),
549            1 => RelType::ExtensionSingle(Box::new(ExtensionSingleRel {
550                common: None,
551                detail: Some(detail),
552                input: Some(inputs_rel.pop().unwrap()),
553            })),
554            _ => RelType::ExtensionMulti(ExtensionMultiRel {
555                common: None,
556                detail: Some(detail),
557                inputs: inputs_rel.into_iter().map(|r| *r).collect(),
558            }),
559        };
560        Ok(Box::new(Rel {
561            rel_type: Some(rel_type),
562        }))
563    }
564
565    fn push_lambda_parameters(
566        &mut self,
567        lambda_parameters: Vec<FieldRef>,
568    ) -> datafusion::common::Result<()> {
569        let lambda_parameters_map = lambda_parameters_map(self, lambda_parameters)?;
570
571        self.lambda_producer
572            .push_lambda_parameters(lambda_parameters_map);
573
574        Ok(())
575    }
576
577    fn pop_lambda_parameters(&mut self) -> datafusion::common::Result<()> {
578        self.lambda_producer.pop_lambda_parameters()
579    }
580
581    fn lambda_variable(&self, name: &str) -> datafusion::common::Result<(u32, i32)> {
582        self.lambda_producer.lambda_variable(name)
583    }
584
585    fn lambda_parameter_type(
586        &self,
587        name: &str,
588    ) -> datafusion::common::Result<substrait::proto::Type> {
589        self.lambda_producer.lambda_parameter_type(name)
590    }
591}
592
593/// Default implementation of lambda related methods of the [SubstraitProducer] trait
594///
595/// Can be embedded into a custom [SubstraitProducer] to implement them
596pub struct DefaultSubstraitLambdaProducer {
597    lambdas_variables: Vec<HashMap<String, (usize, substrait::proto::Type)>>,
598}
599
600impl Default for DefaultSubstraitLambdaProducer {
601    fn default() -> Self {
602        Self::new()
603    }
604}
605
606impl DefaultSubstraitLambdaProducer {
607    pub fn new() -> Self {
608        Self {
609            lambdas_variables: Vec::new(),
610        }
611    }
612
613    /// Note you can construct the `lambda_parameters` argument using [lambda_parameters_map]
614    pub fn push_lambda_parameters(
615        &mut self,
616        lambda_parameters: HashMap<String, (usize, substrait::proto::Type)>,
617    ) {
618        self.lambdas_variables.push(lambda_parameters);
619    }
620
621    pub fn pop_lambda_parameters(&mut self) -> datafusion::common::Result<()> {
622        match self.lambdas_variables.pop() {
623            Some(_) => Ok(()),
624            None => substrait_err!("no lambda_parameters to pop"),
625        }
626    }
627
628    pub fn lambda_variable(&self, name: &str) -> datafusion::common::Result<(u32, i32)> {
629        for (steps_out, lambda_parameters) in
630            self.lambdas_variables.iter().rev().enumerate()
631        {
632            if let Some((field_idx, _type)) = lambda_parameters.get(name) {
633                return Ok((steps_out as u32, *field_idx as i32));
634            }
635        }
636
637        substrait_err!("unknown lambda variable {name}")
638    }
639
640    pub fn lambda_parameter_type(
641        &self,
642        name: &str,
643    ) -> datafusion::common::Result<substrait::proto::Type> {
644        for lambda_parameters in self.lambdas_variables.iter().rev() {
645            if let Some((_field_idx, type_)) = lambda_parameters.get(name) {
646                return Ok(type_.clone());
647            }
648        }
649
650        substrait_err!("unknown lambda variable {name}")
651    }
652}
653
654/// Produces a map of lambda parameters as expected by [DefaultSubstraitLambdaProducer::push_lambda_parameters]
655pub fn lambda_parameters_map(
656    producer: &mut impl SubstraitProducer,
657    lambda_parameters: Vec<FieldRef>,
658) -> datafusion::common::Result<HashMap<String, (usize, substrait::proto::Type)>> {
659    lambda_parameters
660        .into_iter()
661        .enumerate()
662        .map(|(field_idx, field)| {
663            Ok((
664                field.name().clone(),
665                (field_idx, to_substrait_type_from_field(producer, &field)?),
666            ))
667        })
668        .collect::<datafusion::common::Result<_>>()
669}